[스탠포드]Swift 강좌 - MVC 패턴
@정의
1.Model : 앱이 무엇이냐? 무엇에 대한 집합. UI 와 독립되어 있다.
2.Controller : 어떻게 화면에 표시할 것인지에 대해 관심을 가진다.
3.View : 버튼, 라벨 등 UI에 관련된 객체들. Controller 의 통제를 받는다.
@서로의 관계
1.모델과 컨트롤러 : 컨트롤러는 모델에 접근할 수 있다. 하지만 모델은 Notification & KVO 방식을 통해 모델의 변화를 컨트롤러에게 알릴 수 있다.
2.모델과 뷰 : 모델은 UI에 독립적이며 View와 소통할 수 없다.
3.뷰와 컨트롤러 : 컨트롤러는 View에 대해서 outlet을 이용해서 View에게 직접 접근할 수 있다.
뷰는 target - Action 구조로 사용자의 행위에 따라 필요한 함수를 호출 할 수 있다.
또한 구조적으로 미리정해진 방식으로 행위에 대한 요청 (delegate) , 데이터에 대한 요청 (data-source)을 할 수 있다.
@MVC 모델 예제
Card 모델 만들기
Card.swift
1. 클래스와 구조체는 비슷하다.
2. 구조체는 상속이 없다.
3. 구조체는 값 타입이고, 클래스는 참조 타입이다.
4. 값타입은 복사되고, 참조 타입은 포인터를 보낸다.
5. 클래스는 공짜 초기화 메소드가 없다. 구조체 공짜 초기화 메소드가 있다.
6. 구조체 예) 배열, 딕셔너리
import Foundation
/*
이모티콘이나 이미지 같은건 여기에 있으면 안된다.
그것들은 카드를 어떻게 보여주는지와 관련된 내용이다.
*/
//Card 구조체
struct 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 = Card.getUniqueIdentifier()
}
}
Concentration.swift
공개 api 작성: 어떻게 모든 메소드와 변수들을 작성할지 정하는것.
import Foundation
class Concentration {
//필수적인게 뭐가 있을까?
//배열 카드가 있어야 한다.
//var cards = Array<Card>()
var cards = [Card]()
//앞면이 하나인 카드가 존재할 때..를위한 체크
//뒤집혀진 카드의 숫자를 Tracking 하는 변수를 하나 설정.
//어떤 카드도 뒤집혀 있지 않은 상태(== nil)가 있을 수 있기 때문에 옵셔널로 타입을 정합니다.
var indexOfOneAndOnlyFaceUpCard:Int?
//이 게임에서 무엇을 할 수 있을까?
//카드를 고른다.
//카드 뒤집기
func chooseCard(at index : Int){
//매치가 안된 카드일때
if !cards[index].isMatched {
//옵셔널의 값이 존재하고, matchIndex 값이 index와 같지 않다면
if let matchIndex = indexOfOneAndOnlyFaceUpCard , matchIndex != index {
if cards[matchIndex].identifier == cards[index].identifier {
//매치 표시
cards[matchIndex].isMatched = true
cards[index].isMatched = true
}
//해당 카드 앞면 표시
cards[index].isFaceUp = true
//어떤 카드도 뒤집혀있지 않은 상태. 즉, 카드가 하나라도 앞면인 상태가 존재하는 상태
indexOfOneAndOnlyFaceUpCard = nil
}else{
for fileDownIndex in cards.indices {
cards[fileDownIndex].isFaceUp = false
}
cards[index].isFaceUp = true
indexOfOneAndOnlyFaceUpCard = index
}
}
}
//초기화 - 컨트롤러에 넣어준 개수 만큼 카드 생성
init(numberOfPairsOfCards : Int) {
for _ in 1...numberOfPairsOfCards {
let card = Card()
//구조체는 값을 복사한다.
//let matchingCard = card
//카드를 복사해서 카드s 배열에 복사
cards += [card, card]
}
//TODO:카드 섞기
}
}
ViewController.swift
참고
UIKit 라이브러리 : UI를 담당하는 부분. 버튼, 텍스트필드 등. Alerts, Core Motion, WebView, View Hierarchy,
Map Kit, Image Picker, Controls, Camera, Multi-Touch 등
UIViewController 는 UIKit에 포함되어 있다.
import UIKit
class ViewController: UIViewController {
//카운트 Int
var flipCount = 0 {
didSet{
flipCountLabel.text = "count:\(flipCount)"
}
}
//카운트 라벨
@IBOutlet weak var flipCountLabel: UILabel!
//카드 버튼 배열
@IBOutlet var cardButtons: [UIButton]!
//Concentration 클래스의 모든 변수가 초기화 되었으면
//기본 이니셜라이즈를 사용할 수 있다.
//속성 이니셜라이저 변수는 self가 존재하기 전에 실행되어야 한다.
//game 도 초기화 되어야 되고, cardButtons 도 초기화 되어야 한다.
//한 변수가 다른 변수에 의존하고 있다.
//lazy를 사용하면 누가 사용하기 전까지는 초기화 하지 않는다.
//누군가 game을 사용하려할때 초기화 한다.
//lazy가 되면 didSet은 가질 수 없다.
//lazy 변수는 속성 관찰자를 사용할 수 없다.
//이제 컨트롤러가 Model에게 이야기 할 수 있다!
//버튼 개수만큼 카드 생성
lazy var game = Concentration(numberOfPairsOfCards:(cardButtons.count + 1) / 2)
//스위프트에는 모든 인자에는 이름이 있다.
//외부 인자 이름, 내부 인자 이름
@IBAction func touchCard(_ sender: UIButton) {
print("hahah")
flipCount += 1
//버튼이 눌리면 배열얼 보고 눌러진 버튼을 찾아내면
//어떤 카드인지 알 수 있다.
//firstIndex 리턴 값은 옵셔널 값이다.
let cardNumber = cardButtons.firstIndex(of: sender)
//옵셔널의 값 구하기
if let cardNum = cardNumber {
//print("cardNum = \(cardNum)")
//flipCard(withEmoji: emojiChoices[cardNum], on: sender)
//모델이 처리 하도록 처리
game.chooseCard(at: cardNum)
self.updateViewFromModel()
}else{
print("값이 없습니다.")
}
}
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 = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
//뒷면이면
}else{
button.setTitle("", for: .normal)
button.backgroundColor = card.isMatched ? #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0) : #colorLiteral(red: 0.9411764741, green: 0.4980392158, blue: 0.3529411852, alpha: 1)
}
}
}
//이모티콘 배열
var emojiChoices : Array<String> = ["😃","😎" , "🥸" , "😖" ,"😇" ]
//빈 딕셔너리
var emoji = [Int:String]()
//이모티콘 전달 함수
func emoji(for card: Card) -> String{
//딕셔너리의 특정 key 값의 value가 없으면
if emoji[card.identifier] == nil , emojiChoices.count > 0{
let randomIndex = Int(arc4random_uniform(UInt32(emojiChoices.count)))
//이모지 딕셔너리에 삭제한 배열의 값(이모지) 할당
emoji[card.identifier] = emojiChoices.remove(at: randomIndex)
}
//딕셔너리의 key 값이 nil 이면 ? 값 리턴, 아니면 옵셔널의 값 리턴
return emoji[card.identifier] ?? "?"
}
}
/*
모델 : 무엇에 해당하는 부분 언제카드를 뒤집어야 하는지? 등
컨트롤러 : 어떻게.
뷰 : 컨트롤러의 하인들..
*/
참고
www.edwith.org/swiftapp/lecture/26620/?isDesc=false
'아이폰 개발 > Swift' 카테고리의 다른 글
swift Custom UI - 코드로 TabBar&Navigation Controller 만들기 (0) | 2021.03.23 |
---|---|
swift Custom UI - 코드로 UI 만들기&데이터 전달 (0) | 2021.03.23 |
[스탠포드]Swift 강좌 - ios 구조 (0) | 2021.03.09 |
swift 기초 - assert , guard , protocol (1) | 2021.03.05 |
swift 기초 - 옵셔널 체이닝, 타입캐스팅 (0) | 2021.03.05 |