www.youtube.com/watch?v=eHEeWzFP6O4&list=PLpGHT1n4-mAtTj9oywMWoBx0dCGd51_yG&index=4
소스
EmojiMemoryGameView.swift
//
// ContentView.swift
// Memorize
//
// Created by user on 2021/01/15.
//
import SwiftUI
struct EmojiMemoryGameView: View {
@ObservedObject var viewModel: EmojiMemoryGame
var body: some View {
Grid(viewModel.cards) { card in
CardView(card: card).onTapGesture {
viewModel.choose(card: card)
}
.padding(5)
}
.padding()
.foregroundColor(Color.orange)
}
}
struct CardView: View {
var card: MemoryGame<String>.Card
var body: some View {
GeometryReader(content: { geometry in
ZStack {
if card.isFaceUp {
RoundedRectangle(cornerRadius: conrnerRadius)
.fill(Color.white)
RoundedRectangle(cornerRadius: conrnerRadius)
.stroke(lineWidth: edgeLineWidth)
Text(card.content)
} else {
if !card.isMatched {
RoundedRectangle(cornerRadius: conrnerRadius)
.fill()
}
}
}
.font(Font.system(size: fontSize(for: geometry.size)))
})
}
// MARK: - Drawing Constants
let conrnerRadius: CGFloat = 10
let edgeLineWidth: CGFloat = 3
func fontSize(for size: CGSize) -> CGFloat {
min(size.width, size.height) * 0.75
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
EmojiMemoryGameView(viewModel: EmojiMemoryGame())
}
}
MemoryGame.swift
//
// MemoryGame.swift
// Memorize
//
// Created by user on 2021/01/16.
//
import Foundation
struct MemoryGame<CardContent> where CardContent: Equatable {
var cards: Array<Card>
var indexOfTheOneAndOnlyFaceUpCard: Int? {
get {cards.indices.filter { cards[$0].isFaceUp }.only}
set {
for index in cards.indices {
cards[index].isFaceUp = index == newValue
}
}
}
mutating func choose (card: Card){
if let choseIndex = cards.firstIndex(matching: card), !cards[choseIndex].isFaceUp, !cards[choseIndex].isMatched {
if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
if cards[choseIndex].content == cards[potentialMatchIndex].content {
cards[choseIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
}
cards[choseIndex].isFaceUp = true
}else {
indexOfTheOneAndOnlyFaceUpCard = choseIndex
}
}
}
init (numberOfPairsOfCards: Int, cardContentFactory: (Int)-> CardContent) {
cards = Array<Card>()
for pairIndex in 0 ..< numberOfPairsOfCards {
let content = cardContentFactory(pairIndex)
cards.append(Card(content: content, id: pairIndex * 2))
cards.append(Card(content: content, id: pairIndex * 2 + 1 ))
}
}
struct Card: Identifiable {
var isFaceUp: Bool = false
var isMatched: Bool = false
var content: CardContent
var id: Int
}
}
Equatable 프로토콜
- 값이 동일한 지 어떤지를 비교 할 수 있는 타입
class Num {
var num : Int
init(_ num :Int) {
self.num = num
}
}
if A(1) == A(2){} //error!
Where 절
타입 제약은 타입 인자에 요구사항에 제네릭 함수나 타입을 연관하여 정의할 수 있게 한다.
연관된 타입을 위한 요구사항을 정의하는데 유용
Where 절은 연관 타입이 특정 프로토콜에 일치하도록 특정 타입 인자와 연관 타입이 같게 필요하다. Where 절은 where 키워드를 타입 인자 목록 뒤에 위치하며, 연관 타입을 위한 하나 이상의 제약과 타입과 연관 타입 간의 하나 이상의 관계가 따른다.
EmojiMemoryGame.swift
//
// EmojiMemoryGame.swift
// Memorize
//
// Created by user on 2021/01/16.
//
import SwiftUI
class EmojiMemoryGame: ObservableObject {
@Published private var model: MemoryGame<String> = EmojiMemoryGame.createMemoryGame()
static func createMemoryGame() -> MemoryGame<String>{
let emojis: Array<String> = ["😊", "😱", "😎"]
return MemoryGame<String>(numberOfPairsOfCards: emojis.count) { parIndex in
return emojis[parIndex]
}
}
// MARK: - Access to the Model
var cards: Array<MemoryGame<String>.Card> {
model.cards
}
// MARK: - Intent(s)
func choose(card: MemoryGame<String>.Card){
model.choose(card: card)
}
}
Grid.swift
//
// Grid.swift
// Memorize
//
// Created by user on 2021/01/17.
//
import SwiftUI
struct Grid<Item, ItemView>: View where Item: Identifiable, ItemView: View{
var items: [Item]
var viewForItem: (Item) -> ItemView
init (_ items: [Item], viewForItem: @escaping (Item)-> ItemView) {
self.items = items
self.viewForItem = viewForItem
}
var body: some View {
GeometryReader { geometry in
body(for: GridLayout(itemCount: items.count, in: geometry.size))
}
}
func body(for layout: GridLayout) -> some View {
ForEach(items) { item in
body(for: item, in: layout)
}
}
func body(for item: Item, in layout: GridLayout) -> some View {
let index = items.firstIndex(matching: item)!
// return Group {
// if index != nil {
// viewForItem(item)
// .frame(width: layout.itemSize.width, height: layout.itemSize.height)
// .position(layout.location(ofItemAt: index!))
// }
// }
return viewForItem(item)
.frame(width: layout.itemSize.width, height: layout.itemSize.height)
.position(layout.location(ofItemAt: index))
}
}
Array+Identifible.swift
//
// Array+Identifiable.swift
// Memorize
//
// Created by user on 2021/01/17.
//
import Foundation
extension Array where Element: Identifiable {
func firstIndex(matching: Element) -> Int? {
for index in 0..<self.count {
if self[index].id == matching.id {
return index
}
}
return nil
}
}
Array+Only.swift
//
// Array_Only.swift
// Memorize
//
// Created by user on 2021/01/18.
//
import Foundation
extension Array {
var only: Element? {
count == 1 ? first: nil
}
}
Enum
docs.swift.org/swift-book/LanguageGuide/Enumerations.html
Enumerations — The Swift Programming Language (Swift 5.3)
Enumerations An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. If you are familiar with C, you will know that C enumerations assign related names to a set of in
docs.swift.org
열거 관련 값의 그룹에 대한 일반적인 유형을 정의하고 코드 내에서 형태 보증 된 방법으로 그 값으로 작업 가능
각 열거 형 케이스에 대해 값 ( 원시 값 이라고 함 )이 제공되는 경우 값은
문자열, 문자 또는 정수 또는 부동 소수점 유형의 값이 될 수 있습니다.
또는 열거 형 케이스는 공용체 또는 변형이 다른 언어에서 수행하는 것처럼 각기
다른 케이스 값과 함께 저장 될 모든 유형 의 관련 값을 지정할 수 있습니다.
공통된 관련 사례 집합을 하나의 열거의 일부로 정의 할 수 있으며, 각각에는 적절한 유형의 서로 다른 값 집합이 연결되어 있습니다.
Swift의 열거 형은 그 자체로 일류 유형입니다. 열거 형의 현재 값에 대한 추가 정보를 제공하는 계산 된 속성과 열거 형이
나타내는 값과 관련된 기능을 제공하는 인스턴스 메서드와 같이 전통적으로 클래스에서만 지원되는 많은 기능을 채택
열거 형은 초기 케이스 값을 제공하는 이니셜 라이저를 정의 할 수도 있습니다.
원래 구현 이상으로 기능을 확장하도록 확장 할 수 있습니다. 표준 기능을 제공하기 위해 프로토콜을 준수 할 수 있습니다.
기본적으로 Optional 은 2 개의 케이스 가있는 Enum ( none, some )
var x : Int? = 10 <Some (10)>
var x : Int? = nil <None>
Optional 이라는 타입으로 nil 을 처리
옵션은 단지 값의 래퍼입니다.
nil 과 다른 값을 할당 하면 기본 이니셜 라이저가 호출
.Some 상태로 Enum 을 만들고 할당 된 값이 관련 값으로 설정
OptionalType 이 NilLiterableConvertible 프로토콜을 따르기 때문에 가능
프로토콜은 준수 유형에 nil 을 인수로 취하는 이니셜 라이저가 필요함을 정의
Nil Coalescing Operator
c의 값이 nil 경우 우측의 값을 반환
let c: Int? = 10
let d: Int = 0
let e = c ?? d
표현식 d 는 c 안에 저장된 유형과 일치해야합니다 . 경우 d는 문자열 것, 그것은 컴파일되지 않습니다.
let c: Int? = 10
let d: String = “Hello World”
let e = c ?? d // 컴파일되지 않음
Forced Unwrapping
무조건 변수가 있는 상황이 보장된 경우 느낌표(!)를 쓰면 우리가 원하는 Hello을 출력
var optionalString: String? = “Hello”
print(optionalString!)
// 출력 결과: Hello
Enum, Optionals
'IOS' 카테고리의 다른 글
3. IOS 강의 Animation (0) | 2021.01.18 |
---|---|
3. IOS 강의 ViewBuilder Shape ViewModifier (0) | 2021.01.18 |
3. IOS 강의 Reactive UI + Protocols + Layout (0) | 2021.01.17 |
3. IOS 강의 MVVM and the Swift Type System (0) | 2021.01.17 |
3. IOS 강의 Course Logistics and Introduction to SwiftUI (0) | 2021.01.15 |