CS193p 2021 #5 Properties Layout ViewBuilder

影片

課程範例

EmojiMemoryGameView.swift

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


var body: some View {

ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 10))]) {
ForEach(game.cards) { card in
CardView(card: card)
.aspectRatio(2/3, contentMode: .fit)
.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)
Text(card.content)
.font(font(in: geometry.size))
} else if card.isMatched {
shape
.opacity(0)
} 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 = 20
static let lineWidth: CGFloat = 3
static let fontScale: CGFloat = 0.8
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let game = EmojiMemoryGame()
EmojiMemoryGameView(game: game)
.preferredColorScheme(.dark)
EmojiMemoryGameView(game: game)
.preferredColorScheme(.light)
}
}

MemorizeApp.swift

import SwiftUI@main
struct MemorizeApp: App {
private let game = EmojiMemoryGame()

var body: some Scene {
WindowGroup {
EmojiMemoryGameView(game: game)
}
}
}

EmojiMemoryGame.swift

import Foundationclass EmojiMemoryGame: ObservableObject {
typealias Card = MemoryGame<String>.Card

private static let emojis = ["🚲", "🚂", "🚁", "🚜", "🚕", "🏎", "🚑", "🚓", "🚒", "✈️", "🚀", "⛵️", "🛸", "🛶", "🚌", "🏍", "🛺", "🚠", "🛵", "🚗", "🚚", "🚇", "🛻", "🚝"]

private static func createMemoryGame() -> MemoryGame<String> {
MemoryGame<String>(numberOfPairsOfCards: 3) { pairIndex in
emojis[pairIndex]
}
}

@Published private var model = createMemoryGame()

var cards: Array<Card> {
model.cards
}

// MARK: - Intent(s)

func choose(_ card: Card) {
model.choose(card)
}
}

MemoryGame.swift

import Foundationstruct MemoryGame<CardContent> where CardContent: Equatable {
private(set) var cards: Array<Card>
private var indexOfTheOneAndOnlyFaceUpCard: Int? {
get {
cards.indices.filter { cards[$0].isFaceUp }.oneAndOnly
}
set {
cards.indices.forEach {
cards[$0].isFaceUp = ($0 == newValue)
}
}

}


mutating func choose(_ card: Card) {
// 點選已經打開的 card 不會有任何反應
if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }),
!cards[chosenIndex].isFaceUp,
!cards[chosenIndex].isMatched {
if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
if cards[potentialMatchIndex].content == cards[chosenIndex].content {
cards[potentialMatchIndex].isMatched = true
cards[chosenIndex].isMatched = true
}
cards[chosenIndex].isFaceUp = true
} else {
indexOfTheOneAndOnlyFaceUpCard = chosenIndex
}
}
}

init(numberOfPairsOfCards: Int, createCardContent: (Int) -> CardContent) {
cards = []
for pairIndex in 0..<numberOfPairsOfCards {
let content = createCardContent(pairIndex)
cards.append(Card(content: content, id: pairIndex * 2))
cards.append(Card(content: content, id: pairIndex * 2 + 1))
}
}

struct Card: Identifiable {
var isFaceUp = false
var isMatched = false
let content: CardContent
let id: Int
}
}
extension Array {
var oneAndOnly: Element? {
if count == 1 {
return first
} else {
return nil
}
}
}

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

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store