[iOS] @IBOutlet & @IBAction 그리고 옵셔널!

 

 

 

1. @IBOutlet & @IBAction

 

 

 스토리보드를 공부하다 보면 필연적으로 만날 수밖에 없는 것이 있다. 바로 @IBOutlet과 @IBAction이다. 기존에 써봤던 C, C++, Python에서는 보지 못했던 문법이라서 굉장히 낯설고 처음 보는 사람들도 '이게 뭐야?'라고 생각할 것이다. UIKit을 처음 제대로 공부하는 입장에서 처음 보는 사람들도 이해할 수 있게 기록해보려고 한다.

 

1. @IBOutlet

 

 우선 IB는 Interface Builder의 약자로, 말그대로 인터페이스를 만드는데 도움을 주는 역할이다. @IBOutlet은 변수를 지정하는 것과 비슷한 원리로 스토리보드에 추가한 뷰객체(버튼, 텍스트필드, 이미지뷰 등)와 코드를 서로 연결하는 역할을 한다. 또한 각 뷰객체가 가지고 있는 속성에 접근할 수 있다. 여기서 속성이라고 하면 이미지뷰에 어떤 이미지가 들어갈 것인지, border값을 줄 것인지, Label에 어떤 Text를 넣을 것인지 등과 같은 것을 말한다. 따라서, 뷰객체의 커스텀 혹은 모양, 애니메이션 등을 내가 하고 싶은 대로 바꾸기 위해서는 아웃렛 변수 지정이 꼭 필요하다!

 

 @IBOutlet와 뷰객체를 연결하는 방법은 두개가 있다.

 

 첫번째 방법은 스토리보드에서 뷰객체를 마우스 오른쪽 클릭한 후 끌고 와서 코드 부분에 넣는 것이다. 이때 끌고 와서 넣는 위치를

func viewDidLoad() 앞에 넣어줘야 한다. 그 뒤에 넣으면 @IBAction이 생기게 된다.(뷰객체에 따라서 IBAction이 안 생기는 경우도 있음)

 

첫번째 방법

 

 

 코드부분에 끌고 와서 놓게 되면 위와 같은 창이 뜨는데, 여기에 Name을 입력해 주고 Type이 제대로 설정되어 있는지만 확인한 후 Connect를 누르면 자동으로 코드에 @IBOutlet이 선언된 것을 볼 수 있다.

 

 

 

 두번째 방법은 코드상에서 입력을 다 한 다음에 입력한 코드 줄 쪽에 생긴 빈 동그라미를 왼쪽으로 끌어서 스토리보드에 뷰객체 올려두고 놓아주는 방법이다. 

 

두번째 방법

 

두 번째 방법은 사실 굳이 한 자 한 자 다 입력하는 게 더 귀찮고 번거롭지 않을까라고 생각이 들었다. 그러나 한 화면에 여러 개의 뷰객체가 있고 이를 다 연결할 때는 오히려 두 번째 방법이 더 편리하다고 개인적으로 생각이 들었다.

 

 

 

주의 : 스토리보드의 뷰객체를 복사 붙여 넣기를 통해서 복제하는 것은 에러와 버그를 발생시킬 수도 있다. 연결된 @IBOutlet과 @IBAction도 같이 복사가 되기 때문에 주의해야 한다!!

 

2. @IBAction

 

 @IBAction은 Event가 일어난 경우 호출되는 Action을 정의한 것이다. 즉, 사용자의 제스처를 통해 이벤트가 일어난 경우에 실행될 기능을 정의할 때 사용이 된다. 예를 들어 버튼을 눌렀을 때 이미지가 나타나게 하고 싶다면 

@IBAction func ButtonClicked(_ sender: UIButton) {
        
        myImageView.isHidden = false
      
}

 

위와 같은 형식이 되는 것이다. 

 

 

 @IBAction와 뷰객체를 연결하는 방법은 간단하다. 위에서 @IBOutlet을 만들었던 것처럼 @IBAction을 만들 수 있는 뷰객체를 마우스 오른쪽버튼 (매직패드는 손가락 두 개)로 끌고 와서 func viewDidLoad() 뒤 쪽에 놓아주면 된다.

 그 후 name과 Type 그리고 Event를 적절하게 설정해주고 Connect를 누르면 끝이다.

 

 

3. @IBAction에 대한 궁금증

 

  • 애플에서 만든 함수가 아닌 사용자 지정함수(내가 만든 함수)인데 viewDidLoad에 부르지 않아도 왜 작동하는지?
    • 우리가 로직으로 가져올 때, 언제 실행할지(Event) 시점을 이미 정해주어서 따로 부르지 않아도 실행된다.
  • 그럼 @IBOutlet 없이 @IBAction만 있어도 되나?
    • 가능하다!! 다만 @IBOutlet이 없기에 UI 속성은 변경이 불가능하지만, 액션은 작동한다.
  • @IBAction 뒤쪽에 있는 Sender는 무엇인가?
    • Sender는 메서드의 파라미터로 하나의 Action에 여러 뷰객체를 연결했을 때, 사용하면 편하다!

 

 

 

 

2. 옵셔널(Optional)

 

1. 옵셔널이란?

 스위프트에서 새로 도입된 개념으로, 프로그래밍 언어 차원에서 안정성을 최대한 높이고자 사용하는 것이 '옵셔널'이다. 값을 처리하는 과정에서 오류가 발생할 가능성이 있는 값을 옵셔널 타입으로 감싸서 표현하며, 만일 오류가 발생하면 프로그램이 강제 종료된다. (앱이 꺼질 수 있다. 오류가 발생한 경우, nil이라는 값을 반환한다.) 이런 경우를 최대한 피하고자 등장한 개념이다.

 다시 쉽게 말하자면 옵셔널 변수는 값이 있을 수도, 없을 수도 있는 변수를 말한다.

 예를 들어서

var movie: String = "서울의 봄"
print(movie)

 

라는 코드가 있는데, 변수의 할당된 문자열을 제거하면

var movie: String 
print(movie) // 에러

 

movie의 값이 없기 때문에 출력이 안되며 오류가 발생하게 된다. 즉 위와 같이 변수를 선언한다면 값을 넣어주면서 초기화를 해줘야 한다. (Int, Double, String... 등도 같다) 그런데 이때?을 이용해 변수 타입을 옵셔널 타입으로 바꾸면

 

var movie: String?
print(movie)// nil

 

nil로 출력이 된다.

 

 

2. 옵셔널 처리 방법

 

 옵셔널 변수를 처리하는 방법에는 몇 가지가 있다.

 

 

   1. 강제 언래핑 

  값이 100% 문제가 없다고 보장된 경우 사용하는 방법으로, 데이터 타입 뒤에! 을 붙여준다.

var movie: String! = "서울의 봄"

 

 앞서 말했듯 값이 nil이 아닌 것이 보장될 때 사용해야 하며, 만일 nil 값일 때 강제로 언래핑을 하면 앱이 꺼지게 된다.

 

 

 

   2. 옵셔널 바인딩

 안전하게 옵셔널 값을 언래핑하는 방법으로 가장 많이 사용하는 방법들이기도 하다.

 

??(nil 결합 연산자)를 사용하거나 if - else 구문을 보통 이용한다.

 

1. nil 결합 연산자(??)

 

??(nil 결합 연산자)는 로 if - else 구문과 같은 의미이다.

var movie: String? = "서울의 봄"
let currentMovie = movie ?? "영화가 없습니다."

print(currentMovie) //서울의 봄

 

movie에 값이 있다면 그 값 그대로 출력이 되고 

var movie: String? = "서울의 봄"
let currentMovie = movie ?? "영화가 없습니다."

print(currentMovie) //영화가 없습니다.

값이 없다면?? 뒤에 설정해 둔 값이 출력이 된다.

 

 

2. if - else

 

조건문을 통해서 nil일 경우 특정 값을 출력하게 하고, 아닐 시엔 가진 값을 출력하는 방법이다.

 

var movie: String?

if movie != nil {
	print(movie)
} else { //nil 값을 가질 때
	print("영화가 없습니다.")
}

 

이와 비슷한 방법으로는 if let, guard let 구문이 있다.

 

if let

if let은 옵셔널의 값이 존재하는지 확인하고, 확인하면 그 값을 사용하고, nil 값이라면 else 구문을 실행시킨다.

let movie: String? = nil
       
if let movie = movie { //여러개 변수에 그때마다 다른 변수명을 만들 수 없으니 같은 이름을 많이 씀
	print(movie)
} else {
	print("영화가 없어용!")
}

 

위의 코드대로면 movie 값은 nil 값이기에 "영화가 없어용!"이 출력이 될 것이다. 

하지만 if let은 지역변수로만 사용이 가능하기 때문에 구문 밖에서 접근이 불가하다!!

 

만일 옵셔널을 해제해야하는 변수가 많다면 if let 다음에 ,를 활용해서 열거해주면 된다.

if let movie = movie, let age = age, let location = location {
	//code
}

 

 

guard let

guard let은 뒤에 따라 붙는 값이 nil이 아닐 경우 코드가 계속 실행되며, nil일 경우에는 else의 블록 내부 코드를 실행시킨다. 

그리고 else 내부 코드에 return, break, throw 등의 "제어문 전환 명령어'를 사용해야 한다.

if let과는 다르게 else구문이 필수이며, 전역변수로써 사용가능하다. (이미 nil 값이 아님을 확인했으니 편하게 사용 가능!!!)

let movie: String? = "서울의 봄"
                
guard let movie = movie else { 
	print("영화가 없어요!")
	return 
}
        
print(movie) //서울의 봄

 

if let, guard let 둘다 movie = movie 라고 다 적지 않고 movie만 적는 방식으로 생략해줘도 잘 작동한다!

let movie: String? = "서울의 봄"
                
guard let movie else {
	print("영화가 없어요!")
	return 
}
        
print(movie) //서울의 봄

 

 

 

3. 옵셔널 체이닝

 

 nil값을 점검할 때 간결하게 표현할 수 있는 코드이다. 불필요한 코드와 과정을 생략할 수 있다는 특징이 있다.

 

@IBOutlet var menuButton: UIButton?

overrider func viewDidLoad() {
	super.viewDidLoad()
    
    menuButton?.setTitle("버튼", for: .normal)
    munuButton?.layer.borderColor= UIColor.black.cgColor
    menuButton?.backgroundColor = .green
    
}

 

위와 같은 코드가 있을 때 menuButton은 옵셔널 변수이다. 따라서 이 버튼의 속성 등을 제어하기 위해선 위에서 사용했던 방법들을 사용해 언래핑을 해줘야한다. 이때 menuButton에 접근할때마다 ?을 뒤에 붙여줌으로써 다른 방법을 쓰지 않고 언래핑을 하는 방법이다. 만약 menuButton이 nil이라면 nil값이 반환될 것이다.