iOS : Navigation vs Modal (2)
오늘은 iOS 에서 화면을 전환할 때 사용하는 대표적인 두 가지 방법인
- Navigation - Push & Pop
- Modal - Present & Dismiss
중에서 Modal - Present & Dismiss 에 대해 이야기 해보려고 한다.
(Navigation - Push & Pop 에 대해서는 이전 글을 참고)
사실 iOS 개발하시는 분들이 늘상 사용하는 것들이기 때문에 뭘 이렇게 글로 쓰기까지 해야하나(?)라는 생각도 들긴 하지만, 실제로 Navigation, Modal 을 평소에 사용하면서도 그 특성이나 내부 동작에 대해 잘 모르고 있는 분들이 의외로 많다는 것을 알게 되어… 별 것 아니지만 알고 사용하면 큰 도움이 될 만한 내용들을 정리해보려고 한다.
1. Modal Present
우리가 평소에 Modal 방식으로 화면을 띄우기 위해 흔히 사용하는 present 함수는 아래와 같은 형태로 사용 가능하다.
테스트를 진행하기 위해서 Present VC
라는 title 을 가진 UIButton 을 만들어준다.
(그 외의 버튼들 및 이전 소스코드 확인이 필요하면 iOS : Navigation vs Modal (1) 를 참고해주세요 :-) )
그리고 ViewController 코드는 다음과같이 작성해줍니다.
이제 코드를 실행해서 Present VC
동작을 실행하면 아래와 같이 동작하는 것을 확인할 수 있습니다.
2. Modal Dismiss
present 동작을 확인했으니 이제 화면에서 modal VC 를 제거하는 dismiss 함수에 대해 알아보자.
기본적으로는 특별할 것 없이 모두가 아는 것처럼 간단하게 animated 설정과, 필요할 경우 completion closure 를 전달하여 사용 가능하다.
그런데 여기서 정말 많은 개발자들이 간과하고 지나치는 것이 있는데, Apple 공식문서에서 아래 내용을 확인해보자.
위의 red-box 내용이 무엇인고 하니,
- A-VC 에서 present 를 호출하여 B-VC 가 화면에 나타났을 때
- B-VC 에서 dismiss 를 호출하면 B-VC 는 A-VC 에게 해당 동작을 의뢰하고
- A-VC 는 자신이 present 함수를 호출하여 화면에 나타났던, 자신의 presented-VC 인 B-VC 를 dismiss 시킨다.
는 내용이다.
일반적으로 VC 에서 dismiss 를 호출하면 VC 입장에서의 self 를 dismiss 시키는 것이라고 오해하는 경우가 많은데, dismiss 는 본질적으로 VC 가 present 시킨 presented-VC 를 dismiss 시키는 동작이다.
다만 dismiss 함수를 호출한 VC 입장에서 presented-VC 가 없는 경우, 자신의 presenting-VC 에게 해당 dismiss 를 처리하도록 전달하는 것이다.
이 차이를 이해하지 못하면 Modal-Stack 이 여러 개 쌓였을 때, Modal-Stack 내에서 특정 VC 를 선별하여 dismiss 시키는 코드를 작성할 수가 없다.
일단 Modal-Stack 내에서의 Presenting-VC, Presented-VC 의 관계에 대해서는 잠시 후에 더 설명하기로 하고, 일단 dismiss 동작을 구현해보자.
이 글의 초반에 공유했던 코드에 dismissViewController 라는 함수를 추가하고 버튼을 연결해주자.
이렇게 함수 추가 후에 버튼을 연결하고 실행시킨 후에, present 와 dismiss 를 반복해서 동작시키면 아래와 같이 동작하는 것을 확인할 수 있다.
여기서 dismissViewController 함수의 내용을 다음과 같이 수정한 후에 다시 실행시켜 보자. 함수의 내용은 변경되었지만 실행 결과는 완전히 동일한 것을 확인할 수 있을 것이다.
3. VC-Relation on Modal-Stack
이제 우리가 present, dismiss 등을 실행할 때 ViewController 간의 어떤 관계가 생성되어 Modal-Stack 이 구성되는지 알아보자.
아래와 같이 VC 에서 present 를 두 번 호출하여 세 개의 VC 가 Modal-Stack 내에 있다고 가정했을 때
3개의 ViewController 사이에는 다음과 같은 관계가 형성된다.
풀어서 설명하면
- A-VC 에서 present 를 호출해서 B-VC 가 나타났을 때
- A-VC 의 presentedViewController 는 B-VC 를 가리키고
- B-VC 의 presentingViewController 는 A-VC 를 가리킨다.
UIKit 내부를 들여다보면 아래와 같이 UIViewController 내에 optional property 로 각각 정의되어 있는 것을 확인할 수 있고,
Apple 공식문서에서 presentedViewController, presentingViewController 을 찾아보면 아래와 같이 설명하고 있다.
또 이렇게 말로만 설명하면 아쉬우니, VC 가 나타나는 시점에 VC 관련된 정보들을 print 를 해보자.
printVCInfo 함수는 아래와 같이 구현되어 있고 viewDidAppear 시점에 다음과 같은 정보를 console 에 출력해준다.
- 현재 VC 의 title
- 현재 VC 의 navigaitonController 의 정보 (address)
- 현재 VC 의 presentingViewController 정보 (address)
- 현재 VC 의 presentingViewController 의 presentedViewController 의 정보 (address)
아래와 같이
- 앱 실행 후
- present 한 번
- dismiss 한 번
실행했을 경우
console 에는 아래와 같이 print 되어있는 것을 확인할 수 있다.
여기까지 데모를 진행했던 ViewController 의 전체 소스코드는 다음과 같다.
오늘은 iOS 의 화면전환 Flow 중 하나인 Modal Present & Dismiss 에 대해서 알아보았다.
여기까지 읽어주셔서 감사하고,
모든 iOS 개발자에게 행복 있기를~