CS193p 2021 #6 Protocols Shapes

彼得潘的 iOS App Neverland
2 min readJun 3, 2021

--

影片

課程範例

Pie.swift

import SwiftUIstruct Pie: Shape {
var startAngle: Angle
var endAngel: Angle
var clockwise = false

// https://zh.wikipedia.org/zh-tw/三角函数
func path(in rect: CGRect) -> Path {
let center = CGPoint(x: rect.midX, y: rect.midY)
print("path", rect, center)
let radius = min(rect.width, rect.height) / 2
let start = CGPoint(x: center.x + radius * CGFloat(cos(startAngle.radians)), y: center.y + radius * CGFloat(sin(startAngle.radians)))
var path = Path()
path.move(to: center)
path.addLine(to: start)
path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngel, clockwise: !clockwise)
path.addLine(to: center)
return path
}

}

AspectVGrid.swift

import SwiftUIstruct AspectVGrid<Item, ItemView>: View where ItemView: View, Item: Identifiable {


var items: [Item]
var aspectRatio: CGFloat
var content: (Item) -> ItemView

init(items: [Item], aspectRatio: CGFloat, @ViewBuilder content: @escaping (Item) -> ItemView) {
self.items = items
self.aspectRatio = aspectRatio
self.content = content
}


var body: some View {
GeometryReader { geometry in

VStack {
let width = widthThatFits(itemCount: items.count, in: geometry.size, itemAspectRatio: aspectRatio)
LazyVGrid(columns: [adaptiveGridItem(width: width)], content: {
ForEach(items) { item in
content(item).aspectRatio(aspectRatio, contentMode: .fit)
}

})
Spacer(minLength: 0)
}
}
}

private func adaptiveGridItem(width: CGFloat) -> GridItem {
GridItem(.adaptive(minimum: width), spacing: 0)
}

private func widthThatFits(itemCount: Int, in size: CGSize, itemAspectRatio: CGFloat) -> CGFloat {
var columnCount = 1
var rowCount = itemCount
repeat {
let itemWidth = size.width / CGFloat(columnCount)
let itemHeight = itemWidth / itemAspectRatio
if CGFloat(rowCount) * itemHeight < size.height {
break
}
columnCount += 1
rowCount = (itemCount + (columnCount - 1)) / columnCount
//rowCount = (itemCount - 1) / columnCount + 1
} while columnCount < itemCount

if columnCount > itemCount {
columnCount = itemCount
}
return floor(size.width / CGFloat(columnCount))
}
}

EmojiMemoryGameView.swift

import SwiftUIstruct EmojiMemoryGameView: View {
@ObservedObject var game: EmojiMemoryGame


var body: some View {

AspectVGrid(items: game.cards, aspectRatio: 2/3) { card in
if card.isMatched, !card.isFaceUp {
Rectangle()
.opacity(0)
} else {
CardView(card: card)
.padding(4)
.onTapGesture {
game.choose(card)
}
}


}
.foregroundColor(.red)
.padding(.horizontal)
}

}
struct CardView: View {
let card: EmojiMemoryGame.Card


var body: some View {
GeometryReader { geometry in
ZStack {

let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
if card.isFaceUp {
shape
.fill()
.foregroundColor(.white)
shape
.strokeBorder(lineWidth: DrawingConstants.lineWidth)
Pie(startAngle: .degrees(0-90), endAngel: .degrees(110-90))
.padding(5)
.opacity(0.5)
Text(card.content)
.font(font(in: geometry.size))
} else {
shape
.fill()
}
}
}
}

private func font(in size: CGSize) -> Font {
.system(size: min(size.width, size.height) * DrawingConstants.fontScale )
}

private struct DrawingConstants {
static let cornerRadius: CGFloat = 10
static let lineWidth: CGFloat = 3
static let fontScale: CGFloat = 0.7
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let game = EmojiMemoryGame()
game.choose(game.cards.first!)
return EmojiMemoryGameView(game: game)
.preferredColorScheme(.dark)

}
}

MemoryGame.swift,EmojiMemoryGame.swift,MemorizeApp.swift

參考 #5

Reading Assignment III: The Rest of Swift

Assignment III: Set

--

--

彼得潘的 iOS App Neverland
彼得潘的 iOS App Neverland

Written by 彼得潘的 iOS App Neverland

彼得潘的 Swift iOS App 程式設計入門,文組生的 Swift iOS App 程式設計入門,彼得潘的 Flutter 跨平台 App 程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com

No responses yet