이번글은 6. 조건문과 반복문에서 언급한 enum에 대한 글이다.

직접 만드는 자료형중에 하나라고 소개하고싶다.

정수가 여러가지 수를 모아 둔 형태라 하면 이 enum 은 사용자가 원하는것들을 모아둘수있다.

6번글에서 switch 예제로 사용한 코드를 다시보면

    var a = "banana"

    switch a {

      case "banana" : print("banana")

      case "apple" : print("apple")

      case "melon" : print("melon")

      case "lemon" : print("lemon")

      case "peach" : print("peach")

      default: print("다른 종류이다.")

    }

출처: https://wiwi-pe.tistory.com/105?category=976417 [선생님 개발블로그가 하고싶어요.:티스토리]

라고 되어 있는데.

여기서 a는 String이라는 자료형을 가지게된다.

switch는 조건으로 들어온 a가 가질수 있는 모든 값에 대해 case를 지정해줘서 조건을 분기하는 기능을 하는데

default에서 이외의 String값을 처리해주고있는데 오타라던가가 발생하면 항상 default로 빠져버린다.

여기서 만약 a가 enum이였다면 훨씬더 깔끔한 모습으로 안정적이게 바꿀수있다.

a 값이 

banana apple melon lemon peach 중 하나라면 

enum Fruit {
    case banana
    case apple
    case melon
    case lemon
    case peach
}

var a = Fruit.banana
switch a {
    case .banana : print("banana")
    case .apple : print("apple")
    case .melon : print("melon")
    case .lemon : print("lemon")
    case .peach : print("peach")
}

위와 같은 형태로 a값에 대한 오류를 사전에 방지를 할 수 있게된다.

이 이외에도 조건문의 기준으로 사용하기위해 불형 변수를 하나 사용한다하면

var flag = false

if flag { print("참일때 실행해라" }

이 항목을 

enum Some {
    case run
    case stop
}
var flag = Some.run
if flag == .run { print("참일때 실행해라") }

이와 같이 변경해서 사용하면 가독성과 확장성이 좋아진다.

가독성이야 이름때문에 그렇다해도 확장성이 왜 좋아지냐면

만약 flag가 나중에 run또는 stop이 아닌 pause를 가지게 된다면 단순히 Some에서 case pause를 추가해주면 된다. 또다른 flag를 사용하여 연속으로 if문을 중첩하여 분기를 해주는것보다 훨씬 확장성이 좋다.

다시 위의 바나나에 관한 이야기로 돌아가자면 enum자체에도 하나의 특정한 값을 가지게 할수도 있고 함수를 가지게 할 수 도 있는게 스위프트의 장점인데 이에 대해 예제로 하나 퉁치며 이번 enum을 마치려고 한다.

enum Fruit : Int {
    case banana = 12
    case apple
    case melon
    case lemon
    case peach
    
    var stringValue: String {
        get {
            switch self {
            case .apple: return "apple"
            case .banana: return "banana"
            case .lemon: return "lemon"
            case .melon: return "melon"
            case .peach: return "peach"
            }
        }
    }
}


var a = Fruit.lemon
switch a {
    case .banana :
        print("banana")
    case .apple :
        print("apple")
    case .melon :
        print("melon")
    case .lemon :
        print("lemon")
    case .peach :
        print("peach")
}
print(a.rawValue, a.stringValue)

이 코드를 실행해보면

lemon

15 lemon 

와 같은 두줄이 뜨게 되는데 첫줄은 switch문에의한 print이고 두번째 줄은 switch 이후의 print문이다.

enum은 rawValue를 지정해줘서 가져올수 있는데 

rawValue를 위에 banana = 12 로만 주면 아래에 들어가는 값을 자동으로 순서대로 지정된다.

추가로 enum에는 stringValue처럼 값을 지정해주거나 func을 새로 넣어 줄 수 있어 조건문같은와 같이 쓰기 매우 좋은 형태이다.

 

- 변수에 대괄호를 열어 get을 정의해줬는데 이에 대해서는 앞 글에서 다루지 않아 이후에 또 다룰수 있으면 좋겠다.

- 함수에 대해서도 다룬적이없다.

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34

스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35

스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36

스톱워치 만들기 (4) : https://wiwi-pe.tistory.com/37

스톱워치 만들기 (5) : https://wiwi-pe.tistory.com/38

스톱워치 만들기 (6) : https://wiwi-pe.tistory.com/39

 

iOS 개발 튜토리얼1 - 스톱워치 만들기 (6) 스토리보드와 코드 연결하기,함수만들기, 코드로 버튼과 함수 연결하기

스톱 워치 만들기 (1) : https://wiwi-pe.tistory.com/34 스톱워치 만들기 (2) : https://wiwi-pe.tistory.com/35 스톱워치 만들기 (3) : https://wiwi-pe.tistory.com/36 스톱워치 만들기 (4) : https://wiwi-pe...

wiwi-pe.tistory.com

 

 

 

저번편에서 버튼에서 하나 잊은게있었습니다.

 

isEnabled 속성입니다.

 

어제 소스 그대로 시뮬레이터로 돌려보면 버튼중에 startButton을 누르면 콘솔에 10이 뜨고

그외에는 UI적으로 눌렀다는 표시만 나올겁니다.

이 눌렀다는 표현이 나온다는거 자체를 막아야할때가있습니다.

 

그때 쓰는게 바로 isEnabled 속성입니다. 기본은 true로 당연히 모든버튼이 누를수있도록 되어있습니다.

애플문서의 설명은 저번 포스트에서 있을거에요 넘어가겠습니다.

 

스톱워치라하면 startButton을 누르기전까진 랩타임이 기록되면 안되고 스톱버튼도 동작이 무의미하겠죠?

 

오히려 스톱버튼이 불필요한 동작으로인해 다른 문제가 발생할여지도 있습니다.

 

그래서 startButton을 눌러 시작상태가 될때만 이 둘이 동작가능하도록 하겠습니다.

 

우선은 checkButton만 해보죠

 

저번 소스의  viewDIdLoad함수안에 있는 

 

startButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)

소스 바로 아래라인에 추가해주세요

 

checkButton.isEnabled = false

 

이제 시뮬레이터로 실행을 시켜보면

 

위 그림처럼 Check 버튼자체가 눌려지지 않게됩니다

 

추가로 stop버튼도 

 

이제 다른 버튼에 대해 기능을 달아주기전에 enum 변수에대해 조금 알아보고갑시다

 

열거형 변수라고 부르는 enum은 

단순히 여러개의 종류를 한묶음으로 묶어줄때 사용합니다.

 

형태는 아래와 같습니다

 

enum 이름 {

    case 첫번째항목

    case 두번째항목

}

 

예시로 하나 들자면

 

enum Icecream {

    case vanilla

    case strawberry

    case cherry

    case chocolate

}

이렇게 아이스크림 종류가 있겠네요.

 

사실 굳이 이걸 안써도 되지만

 

 

swift에서는 이 enum과의 switch의 상성이 매우 좋습니다.

 

위의 아이스크림을 아래와같이 할 수 있어요

 

var selectIce:Icecream 

이렇게 Icecream이라는 enum형식을 가진 selectIce라는 변수가 있다면

 

switch selectIce {

    case .vanilla :

        print("바닐라를 선택하셨습니다.")

    case .strawbrerry :

        print("딸기를 선택하셨습니다.")

    case .cherry :

        print("체리를 선택하셨습니다.")

    case .chocolate :

        print("초콜릿을 선택하셨습니다.")

}

 

이런식으로 모든 switch에서 나올수 있는 항목을 enum으로 지정한 항목만 가능하도록 할 수 있습니다.

물론 다른언어처럼 중간에 선택할 필요없는 항목은 default 라는걸로 생략 할 수 있어요

이번 튜토리얼에서 버튼들의 기능을 하나의 함수로 묶는다고 했는데

바로 이때 쓸겁니다.

버튼 종류를 한정시켜놓고(enum) switch문으로 나눌꺼에요

 

MainVC 클래스 내부 상단에 아래와같이 정의해줍시다

 

    enum ButtonTag:Int {

        case start = 10

        case check = 20

        case stop = 30

        case reset = 40

    }

  

갑자기 각 case에 값이 달렸네요!

네 스위프트에서는 enum의 가능성은 무한한합니다!

 

공부해 나가다보면 함수도 달고 이것저것 달고 마치 하나의 클래스를 지정하듯한 모습을 보실수 있을겁니다만

튜토리얼에서는, 그리고 제가 짜는 코드에서는 값이 달리는것 외에는 없겠네요.

 

아직도 주니어 개발자이고 여태 해본 프로젝트에서는 더이상의 기능은 필요가 없었어요.

 

값을 지정해주려면 enum의 이름 옆에 타입을 적어주고

각 case 에 그 타입에 맞는 값을 넣어주시면 됩니다.

 

추가로 enum에 값을 넣어주시면 그 값에 해당하는 내용이 있는지 검사해볼수 있습니다

 

enum의 이름을 적어서 생성자같이 해주면됩니다!

ButtonTag(rawValue: 찾는값) 이라고 해주면 옵셔녈 변수로 값을반환해줍니다.

 

또 다른 언어에는 없는 변수가 나왔네요.

옵셔녈 변수는 

선언할때는 변수타입뒤에 '?'를 붙여서 선언해줍니다

이는 값이 있을수도 있고 없을수도 있어! 라고 해주는건데

하나의 변수에 값에 대한 지정을 없다 or 값 으로 해줄 수 있어 좀더 안정적인 프로그래밍이 가능해도록 해주는 녀석입니다

이 옵셔널이라는건 일종의 포장입니다 

(원래값을 가진 변수) 를 (옵셔널(원래값을 가진 변수)) 이 되어 

옵셔널을 풀어줘야만 원래값을 볼 수 있습니다

이 원래값을 가진 변수 부분에 사실 값이 없을 수 있을경우( null값, swift에서는 nil이라 부릅니다)

이 옵셔녈을 바로 풀어주면 nil이 나오게 됩니다.

 

옵셔녈을 풀어주는 방법은 여러가지 있는데 여기서 두가지만 소개하자면

b라는 옵셔널 변수가 있다면 이를 사용하기 위해선 

첫번째

if안에서 변수를 선언해서 사용가능합니다.

if let a = b {

    내부에서만 사용가능

}

if의 조건문 자리에 b를 할당하는 변수를 선언하게되면 이 b가 할당가능하면( 옵셔널내부에 값이 존재하면) a라는 변수에  b가 가진 원래값을 넣고 아니라면 else로 빠져나가게 되는 조건문이되겠습니다 

 

두번째

b가 nil일때 대신할 값을 지정 

let a = b ?? c

 옵셔널값의뒤에 물음표 두개를 붙이면

b가 nil일때 c를 a에 대신넣는다 라는게 됩니다.

 

물론 c와 b의 원래값의 타입은 같아야합니다.

 

옵셔널에대한 더 자세한 설명은

https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html

링크에서

Optionals

항목을 찾아 읽어주세요!

 

그리고 enum은

https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

링크에 더 자세한 설명이 되어있습니다

 

 

 

그럼 잠시 enum은 멈춰두고

 

함수에대해서 이야기 할겁니다.

 

이 튜토리얼의 대상이라면 이미 다른 언어나 혹은 같은 언어로 이미 함수에대해 많이 알고 계실거라 생각됩니다.

 

파이썬이라면

def 함수이름() :

  함수내용

 

자바라면

void 함수이름() {

}

 

그리고 스위프트는(4 이상)

 

func 함수이름() {

}

입니다만 저번편을따라해보신분은 이미 buttonAction함수를 생성하셨을것이고

거기에서 살짝 이야기를 했습니다.

 

좀더 자세히 보자면 함수 형태는 아래와같습니다.

접두사 func 함수이름(파라미터 닉네임 받는 파라미터 : 파라미터 타입) -> 반환타입 {

}

저번포트스에서

@objc func buttonAction(_ sender:UIButotn) {

}

이라고 함수를 하나 만들었는데

@objc가 바로 접두사 쪽( 본래 명칭이 아닐 수 있습니다..;;)

그리고 함수를 뜻하는 func

함수이름은 buttonAction

파라미터는 , 로 구분하는데 하나만 있고

이 파라미터의 닉네임은 _ 로 생략한다.

파라미터명은 sender 이고

파라미터의 타입은 UIButton이다

 

이함수의 반환형은 Void로 없다 없을 경우 저렇게 파라미터위치 옆을 비워둘수있습니다.

 

사실 buttonAction은 이런거죠

    @objc func buttonAction(_ sender:UIButton) -> Void {

        print(sender.tag)

    }

 

 

 

만들 함수는 이전 포스트에서는 버튼에대한 내용을 하나하나

viewDidLoad 함수에서 해줬는데 이에대한 중복을 조금 줄일겁니다.

 

아래와같이 MainVC클래스 내부에 만들어주세요!

 

    func setButton(button:UIButton, tag:ButtonTag){

        button.addTarget(self, action: #selector(buttonAction(_:)), for: .touchUpInside)

        button.tag = tag.rawValue

    }

 

이 setButton이라는 함수는 UIButton과 ButtonTag라는 enum형식 변수를 받아 받은 버튼에대한 속성을 지정해주는 함수입니다

이전

        startButton.tag = 10

        startButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)

를 함수로 만들어준거에요

라인 순서만좀 달라졌네요

이제 이 만든 함수를 이용해 버튼들을 완성시켜봅시다

 

viewDidLoad()함수에서 이전에 지정해준 startButton에 대한 내용을 지우고 아래와같이 완성시켜주세요

 

    override func viewDidLoad() {

        super.viewDidLoad()

        setButton(button: startButton, tag: .start)

        setButton(button: checkButton, tag: .check)

        setButton(button: stopButton, tag: .stop)

        setButton(button: resetButton, tag: .reset)
        checkButton.isEnabled = false

    }

 

현재로서는 viewDidLoad함수는 위와같아야합니다

이러게하면 이제 모든 버튼이 buttonAction함수와 연결이 된겁니다.

 

이제 buttonAction함수를 좀 채워줄꺼에요

이전 Print를 적어준건 지우고 아래와같이 작성해줍시다.

    @objc func buttonAction(_ button:UIButton) {

        if let select = ButtonTag(rawValue: button.tag) {

            switch select {

            case .start: startAction()

            case .check: checkAction()

            case .reset: resetAction()

            case .stop : stopAction()

            }

        } else {

            print("존재하지않는 태그를 가진 버튼의 입력이 들어왔습니다.")

        }

    }

 

 

if let select는 위에서 설명했던데로 ButtonTag(rawValue:)의 반환값이 옵셔녈이라 사용해주기위해 쓴겁니다

그리고 내부는 start, check, rest,stop버튼 항목이 각각 연결해준 함수들이있습니다

 

이 함수들은 아직 안만들어줘서 xcode에서 빨간 줄이 그어질겁니다

 

 

이제 이 존재하지않다고 빨간줄 뜨는 함수들을 다 만들어줄꺼에요

MainVC내부에 아래와같이 추가해줍시다

    
    func startAction() {
        print("start")
        startButton.isEnabled = false
        checkButton.isEnabled = true
        stopButton.isEnabled = true
    }
    
    func checkAction() {
        print("check")
    }
    
    func stopAction() {
        print("stop")
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = false
    }
    
    func resetAction() {
        print("reset")
        startButton.isEnabled = true
        checkButton.isEnabled = false
        stopButton.isEnabled = true
    }

각버튼이 눌리면 무슨버튼인지 콘솔에 출력해줄것이고

 

startAction이 실행되면

기존에 작동을 안하게 해뒀던 check와 앞으로 stop 상태에 들어가면 중지시킬 stop 버튼을 동작하게 해주고

계속해서 startButton이 안눌리도록 startButton의 isEnabled를 false로 설정해줍니다

 

 쭉쭉 이런식으로

stop과 start. 거의 토글식으로 

reset은 전부 처음으로 돌려주고 하는식으로 해줄꺼에요

 

그럼 이제 돌려봅시다

 

 

잘 동작하네요!

 

그럼 다음편에서는 시간이 흐르도록 해봅시다

+ Recent posts