
먼저 한 행씩 카드들을 드래그로 선택한 다음 하단의 동그라미로 표시해놓은 embed 버튼을 눌러서 stack 으로 embed 해주고 오른쪽 설정창처럼 설정해준다(spacing 은 standard value). 이걸 2번 더 반복하고 나서 스택 3개를 하나로 합쳐주면 스택 뷰 생성은 완료
이제 위치를 잡아줄 건데, 스택뷰를 일단 왼쪽 상단에 정렬해준 다음, 스택뷰를 선택한 상태에서 ctrl 키를 누르고 전체 view 에 마우스를 끌고 가면 저 까만 팝업 창이 뜬다. 현재 설정된대로 선택해주면 끝. 하단의 경우 flipCountLabel 에 고정해주고, 셋팅을 아래 사진처럼 해줬다.

0.5...15.25 의 경우 CountableRange 가 아니라 그냥 Range 인데, 얼마씩 이동해야할 지 모르기 때문이다. for i in stride(from: 0.5, through: 12.25, by: 0.3) {
// do sth
}
어떠한 프로퍼티가 다른 프로퍼티를 기반으로 도출된다면, 연산 프로퍼티로 선언함으로써 자동으로 동기화하여 에러 가능성을 줄일 수 있다.
예를 들어서 Concentration 에서 indexOfOneAndOnlyFaceUpCard 프로퍼티는 get 할 때 cards 를 확인하면 알 수 있는 정보이므로 연산 프로퍼티로 만들면 더 쉽게 cards 와 동기화할 수 있다.
set 할 때도 현재 card의 인덱스가 newValue 와 일치하는 경우에만 isFaceup 프로퍼티를 true 로 나머지의 경우 false 로 하도록 해주면 코드도 더욱 깔끔해진다!
class Concentration {
private var indexOfOneAndOnlyFaceUpCard: Int? {
get {
var foundIndex: Int?
for index in cards.indices {
if cards[index].isFaceUp {
if foundIndex == nil {
foundIndex = index
} else {
foundIndex = nil
}
}
}
return foundIndex
}
set {
for index in cards.indices {
cards[index].isFaceUp = (index == newValue)
}
}
}
}
assert(_:_:file:line:) 는 지정한 조건이 거짓일때 프로그램이 터지면서 디버거에 에러 발생 지점을 표시하는 함수로, 앱스토어에 출시하면 무시되나 개발 중에는 매우 유용하다. 특히, access control 과 같이 API 를 보호하는 용도로 쓸 수 있다.
chooseCard(at:) 메서드의 경우 카드의 인덱스를 인자로 받기 때문에 0보다 작은 값이 들어오면 안된다. 따라서 0보다 작은 값이 들어오면 assert(_:_:file:line:) 가 호출되게 함으로써 조건을 명확히 할 수 있다.
class Concentration {
func chooseCard(at index: Int) {
assert(cards.indices.contains(index), "Concentration.chooseCard(at: \(index)) : Choosen index out of range")
if !cards[index].isMatched {
if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
cards[index].isFaceUp = true
if cards[matchIndex].identifier == cards[index].identifier {
cards[matchIndex].isMatched = true
cards[index].isMatched = true
}
} else {
indexOfOneAndOnlyFaceUpCard = index
}
}
}
}
associated value 를 가질 수 있으며, 튜플과 유사하게 get/set 할 때 각각 이름을 지정할 수 있다. 단, 값 자체는 초기화할때만 지정 가능하고 이후에는 변경할 수 없다.
메서드와 연산 프로퍼티를 가질 수 있다
스스로를 변경하는 메서드를 가질 수도 있는데 앞에 mutating 을 붙여줘야 한다! mutating 키워드를 앞에 붙여줘야 하는 이유는 enum 이 copy on write(이하 c.o.w) 를 하는 value type 이므로 Swift 가 c.o.w 를 할 수 있도록 어떤 메서드들이 write 할 수 있는 지(즉, 변화를 일으키는 지) 알려줘야 하기 때문
enum FastFoodMenuItem {
mutating func switchToBeingACookie() {
self = .cookie
}
}
ctrl + drag to pin to the top
safe area : area that doesn't overlap w/ other UI

leading space to safe area : lang like hebrew come from the right

for i in stride(from: 0.5, through: 12.25, by: 0.3) {
// do sth
}
36분 : when do we use computed properties instead of to methods that get and set ? A: when it looks like a var to us
enum FastFoodMenuItem {
mutating func switchToBeingACookie() {
self = .cookie
}
}

