티스토리 뷰

기존 구조 개선


기존은 버튼을 하나 추가 할 때마다 함수를 하나씩 추가하는 구조였습니다.

 

기존 코드

    var flipCount = 0 {
        didSet {
            flipCountLabel.text = "Flips: \(flipCount)"
        }
    }
    
    @IBOutlet weak var flipCountLabel: UILabel!
    
    @IBAction func touchCard(_ sender: UIButton) {
        flipCount += 1
        flipCard(withEmoji: "👻", on: sender)
    }
    
    @IBAction func touchSecondCard(_ sender: UIButton) {
        flipCount += 1
        flipCard(withEmoji: "🎃", on: sender)
    }

버튼을 추가하게 될 경우 touchCard, touchSeconCard, ... 이와 같은 함수가 계속 늘어나게 될 것입니다.

 

확장성을 위해 구조를 개선해도록 합시다

 

먼저 touchSeconCard 함수를 삭제합니다.

다음, 호박 버튼을 우클릭하여 기존에 연결된 touchSecondCard 연결을 해제시킵니다.

x를 눌러 해제 시킵니다.

 

Ctrl 키를 누른 상태에서 호박 버튼을 드래그하여 touchCard 함수와 연결시킵니다.

 

다시 카드를 복사하여 4개로 늘립니다.

touchCard에 각 버튼들이 연결되어 있는지 확인합니다.

 

Outlet Collection 사용하기


버튼을 컨트롤 드래그하여 코드와 연결할 때 Outlet, Action  그리고 Outlet Collection이 있습니다.

세번째에 있는 Outlet Collection

버튼 하나를 Ctrl+드래그하여 코드에 삽입합시다.

변수명은cardButons로 일부러 t를 하나 뺐습니다.

완료 후 아래의 코드가 생성됩니다.

@IBOutlet var cardButons: [UIButton]!

[UIButton]은 UIButton의 배열 타입입니다.

Array<UIButton>와 동일하며 "[타입]"은 배열의 축약형입니다.

 

오타로 입력된 cardButons를 수정해봅시다.

코드의 cardButons를 cardButtons로 수정하게 되면 버튼과의 연결에 문제가 생깁니다.

cardButons로 아직 변경이 안되어 있습니다.

빌드시에도 아래와 같은 로그가 나오게 되며 앱이 종료됩니다.

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Concentration.ViewController 0x123f087b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key cardButons.'

 

Undo를 하여 cardButons로 복원시킨 후 다시 수정해봅시다.

Cmd키를 누르고 cardButons를 클릭하면 아래와 같은 창이 뜹니다.

Rename을 선택합시다.

Rename을 선택시 아래와 같은 창이 뜹니다.

swift와 storyboard 파일에서 사용되는 것을 알 수 있습니다.

cartButtons로 변경 후 Enter키를 입력합시다.

Referencing Outlet Collection의 이름과 swift 파일내 변수이름이 정상적으로 변경된 것을 알 수 있습니다.

변경이 안된다면 버튼이 연결된 부분을 해제 후 변수이름을 변경 한 뒤 해당 변수로 재연결해도 됩니다.

 

남은 버튼들을 cardButtons 변수와 연결해봅시다.

Ctrl키를 누른 채로 View Control 버튼(가장 좌측)을 클릭 드래그하여 버튼에서 놓습니다.

아래와 같은 창이 뜨는데 cardButtons를 선택합시다.

cardButtons를 선택합시다.

테스트를 위해 우측 하단 버튼을 제외하고 다 연결해줍니다.

4번째 버튼은 연결되지 않아 색상이 그대로이고 Butt 텍스트가 안보이고 있습니다.

 

버튼을 눌렀을때 어느 버튼이 눌렸지는 확인을 위해 함수를 수정해줍시다.

@IBAction func touchCard(_ sender: UIButton) {
    flipCount += 1
    let cardNumber = cardButtons.firstIndex(of: sender)
    print("cardNumber = \(cardNumber)")
}

앱을 실행후 버튼을 누르게 되면 아래와 같은 로그가 찍히게 됩니다.

cardNumber = Optional(0)
cardNumber = Optional(1)
cardNumber = Optional(2)
cardNumber = nil

먼저, print에 입력 안한 "Optaional"이라는 문자열이 갑자기 출력되어 있습니다.

Index를 알기 위해 사용한 firstIndex라는 함수의 설명을 보면 아래와 같습니다.

리턴값이 Int가 아닌 Int? 입니다.

리턴 타입을 보면 Int 뒤에 ?가 붙은 걸 알 수 있습니다. 옵셔널 타입이라는 의미입니다.

Int와 Int?는 다른 타입입니다.

옵셔널은 2가지 상태를 나타내는 타입입니다.

상태는 Set, Not Set 두가지입니다. 열거형으로 볼 수 있습니다.

Swift 열거형은 타입에 따라 값을 가질수 있습니다.

Int?의 상태가 Set인 경우에는 Int값을 가지며 Not Set인 경우에는 nil입니다.

nil은 Swift에서 설정되지 않은 옵셔널 값을 나타냅니다.

 

Int값을 가져오려면 어떻게 해야할까요?

먼저 !를 붙여줍니다. 옵셔널의 값이 있다고 가정하고 값을 가져오는 방법입니다.

cardNumber 타입에서 ?가 사라진것을 알 수 있습니다.

시뮬레이터에서 버튼을 선택하면 아래와 같은 로그가 출력됩니다.

cardNumber = 0
cardNumber = 1
cardNumber = 2
Concentration/ViewController.swift:24: Fatal error: Unexpectedly found nil while unwrapping an Optional value

숫자 출력시 Optional이라는 문자가 사라졌는걸 알 수 있습니다.

하지만, 4번째 버튼 선택시 크래시가 발생하여 앱이 종료가 됩니다.

설정된 옵셔널 값이 없는데 !를 통하여 강제로 값을 가져오면서 발생하는 에러입니다.

 

옵셔널을 안전하게 사용하는 두번재 방법입니다.

if 문을 사용하고 true인 경우에만 값을 사용하는 방법입니다.

@IBAction func touchCard(_ sender: UIButton) {
    flipCount += 1
    if let cardNumber = cardButtons.firstIndex(of: sender) {
        print("cardNumber = \(cardNumber)")
    } else {
        print("chosen card was not in cardButtons")
    }
}

만약 true라면 if문 내부에서는 cardNumber값을 사용할 수 있습니다.

nil이라면 if가 false로 처리되면서 else 내부 코드가 실행됩니다.

위 코드를 실행시 후 버튼을 순서대로 선택시 아래와 같은 로그가 출력됩니다.

cardNumber = 0
cardNumber = 1
cardNumber = 2
chosen card was not in cardButtons

4번째 버튼을 정상적으로 연결한 뒤 버튼에 사용할 이모지를 추가해봅시다.

 

아래의 코드를 삽입해줍니다.

var emojiChoices: Array<String> = ["🎃","👻","🎃","👻"]

아래와 같이 배열 축약형으로 변경이 가능합니다.( 세 코드 모두 동일한 코드입니다. )

var emojiChoices: Array<String> = ["🎃","👻","🎃","👻"]
var emojiChoices: [String] = ["🎃","👻","🎃","👻"]
var emojiChoices = ["🎃","👻","🎃","👻"]

 

이제 버튼을 눌렀을때의 코드를 수정해봅시다.

@IBOutlet var cardButtons: Array<UIButton>!
    
var emojiChoices: Array<String> = ["🎃","👻","🎃","👻"]
    
@IBAction func touchCard(_ sender: UIButton) {
    flipCount += 1
    if let cardNumber = cardButtons.firstIndex(of: sender) {
        flipCard(withEmoji: emojiChoices[cardNumber], on: sender)
    } else {
        print("chosen card was not in cardButtons")
    }
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함