Chapter 2
- StoryBoard : View
- ViewController : Controller
- Concentration : Model
- Card : Struct
✔︎ MVC 구조
- M : Model - UI와 관련없는(import Foundation) 코드, 즉 Controller가 명령을 내리면 무엇을 보일지 여부
- V : View - UI만으로 존재 (StoryBoard)
- C : Controller - View와 Model 사이의 소통담당(import UIKit), 어떻게 화면에 표시할지 여부
- C -> M, V : 직접적으로 접근 가능
- V -> C
- delegate : 행위에 대한 요청
- data source : 데이터에 대한 요청
- M -> C : Notification & KVO
✔︎ 목표
- ViewController 에서 UI와 관련없는 코드를 Model로 분리하자
- Struct를 사용하여 Card만의 속성을 설정하자
- Card를 사용하며 UI와 관련없는 게임관련 동작 메소드를 Concentration에 분리하자
- View를 수정하여도 영향없이 동작가능한 Model를 통해 게임을 구현하자
✔︎ 구현 내용
- struct Card : isFaceUp(앞면 여부), isMatched(쌍이 찾아졌는지 여부), identifier(카드 구별값) 속성들을 설정하자
- struct Card : static 변수, 메소드를 통해 Card 타입자체의 identifier 값을 설정하자
- class Concentration : cards: [Card]를 통해 Controller 에서 접근하도록 만들자
- class Concentration : init(카드수: Int)를 통해 cards를 설정하도록 하자
- class Concentration : chooseCard(index: Int) 메소드를 통해 카드가 선택되었을 때 Card의 상태를 설정하는 메소드를 만들자
- isMatched = true 인 카드가 선택되었을 경우 모든 메소드 무시
- 선택된 카드의 공동 행동 : isFaceUp = true 설정
- 두장째 뒤집어진 경우
- 1. 매칭이 된 경우 -> 두 카드 모두 isMatched = true 설정, 확인카드값 nil 설정
- 2. 매칭이 안된 경우 -> 확인카드값 nil 설정
- 카드 한장만 뒤집어진 경우 -> 모든 카드 isFaceUp = false 설정, 해당 index 카드만 true 설정, 확인카드값 index 설정
- ViewController : Model 접근하기 위한 변수 game 설정 (Concentration의 init 설정)
- (전체카드수+1)/2 가지수의 카드종류 설정
- 사용될 때 초기화가 진행되는 lazy 특징 사용
- ViewController : touchCard 메소드 수정
- game.chooseCard 메소드 사용
- Model이 수정됨에 따른 View 수정을 위한 메소드인 updateViewFromModel() 실행
- ViewController : updateViewFromModel() 메소드 추가
- game.cards의 각 isFaceUp 값에 따라 해당 button의 앞, 뒷면 여부 설정
- isMatched 값에 따라 숨기기 설정
- ViewController : emoji 메소드 추가
- 기존 emojiChoices 배열에서 랜덤으로 하나씩 추출하여 Dictiorary 형태인 emoji에 데이터 추가
- Dictionary의 key 값인 클릭된 Button의 identifier 값을 통해 String 형태인 emoji값 반환
✔︎ 새롭게 알게된 기능들
- static var, func : 정적 변수, 메소드라 부르며 struct 타입에 관한 변수, 메소드의 특징을 지닌다
이를 통해 동일 struct 간의 구별과 같은 기능에 사용이 가능하다!
static var identifierFactory = 0
static func getUniqueIdentifier() -> Int {
identifierFactory += 1
return identifierFactory
}
init() {
self.identifier = CP2_Card.getUniqueIdentifier()
}
- 배열.indices를 통핸 for문 형식 : for i in 0..<Array.count -> for i in Array.indices 형식으로 사용이 가능
for flipDownIndex in cards.indices {
cards[flipDownIndex].isFaceUp = false
}
- 배열.append(값1), 배열.append(값2) -> 배열 += [값1, 값2] 형식으로 한번에 추가 가능
cards += [card, card]
- lazy var 변수 : 초기화에 사용되는 변수의 초기화가 먼저 필요한 경우에 사용 가능 (초기화 순서 미루기)
lazy var game = CP2_Concentration(numberOfPairsOfCards: (cardButtons.count+1)/2)
- 랜덤숫자 추출 메소드 : arc4random_uniform
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
✔︎ 동작 화면
✔︎ 전체 코드
- Card
import Foundation
struct CP2_Card
{
var isFaceUp = false
var isMatched = false
var identifier: Int
static var identifierFactory = 0
static func getUniqueIdentifier() -> Int {
identifierFactory += 1
return identifierFactory
}
init() {
self.identifier = CP2_Card.getUniqueIdentifier()
}
}
- Concentration
import Foundation
class CP2_Concentration
{
var cards: [CP2_Card] = []
var indexOfOneAndOnlyFaceUpCard: Int?
func chooseCard(at index: Int) {
if !cards[index].isMatched {
if let matchIndex = indexOfOneAndOnlyFaceUpCard, matchIndex != index {
// check if cards match
if cards[matchIndex].identifier == cards[index].identifier {
cards[matchIndex].isMatched = true
cards[index].isMatched = true
}
cards[index].isFaceUp = true
indexOfOneAndOnlyFaceUpCard = nil
} else {
// either no cards or 2 cards are face up
for flipDownIndex in cards.indices {
cards[flipDownIndex].isFaceUp = false
}
cards[index].isFaceUp = true
indexOfOneAndOnlyFaceUpCard = index
}
}
}
init(numberOfPairsOfCards: Int) {
for _ in 1...numberOfPairsOfCards {
let card = CP2_Card()
cards += [card, card]
}
// TODO: Shuffle the cards
}
}
- ViewController
import UIKit
class CP2_ViewController: UIViewController {
lazy var game = CP2_Concentration(numberOfPairsOfCards: (cardButtons.count+1)/2) //사용될 때 초기화가 진행, didSet은 사용 불가
var flipCount = 0 {
didSet { flipCountLabel.text = "Flips: \(flipCount)" }
}
var emojiChoices: [String] = ["👻", "🎃", "😈", "👽", "💀", "🤢", "🤡", "💩"]
@IBOutlet var cardButtons: [UIButton]!
@IBOutlet var flipCountLabel: UILabel!
@IBAction func touchCard(_ sender: UIButton) {
flipCount += 1
if let cardNumber = cardButtons.firstIndex(of: sender) {
game.chooseCard(at: cardNumber)
updateViewFromModel()
} else {
print("chosen card was not in cardButtons")
}
}
func updateViewFromModel() {
for index in cardButtons.indices { //배열 -> 셀수있는 구간
let button = cardButtons[index]
let card = game.cards[index]
if card.isFaceUp {
button.setTitle(emoji(for: card), for: .normal)
button.backgroundColor = UIColor.white
} else {
button.setTitle("", for: .normal)
button.backgroundColor = card.isMatched ? UIColor.clear : UIColor.orange
}
}
}
var emoji : [Int:String] = [:]
func emoji(for card: CP2_Card) -> String {
if(emoji[card.identifier] == nil), emojiChoices.count > 0 {
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)
}
return emoji[card.identifier] ?? "?"
}
}
✔︎ 깃허브 : https://github.com/FreeDeveloper97/iOS_Stanford_Univ
✔︎ 과제내용
- 카드를 랜덤으로 섞는 작업
- 게임이 종료되었을 때 작업
'iOS 개발자 > iOS Stanford Univ' 카테고리의 다른 글
[iOS 스탠포드] Chapter3 (0) | 2021.07.07 |
---|---|
[iOS 스탠포드] Assignment 1 : Concentration (0) | 2021.07.05 |
[iOS 스탠포드] Chapter1 (0) | 2021.06.18 |