왜 갑자기 UIScene이 등장했을까?
iOS 13이 출시되면서 가장 큰 변화 중 하나가 바로 UIScene의 도입이었습니다.
왜 Apple이 기존에 잘 작동하던 AppDelegate 구조를 바꿔가며 이런 복잡한 시스템을 도입했을까요? 🤔
핵심은 iPadOS의 멀티윈도우 지원 때문입니다.
기존 iOS에서는 하나의 앱이 하나의 화면(window)만 가질 수 있었지만, iPadOS부터는 하나의 앱이 여러 개의 윈도우를 동시에 띄울 수 있게 되었거든요.
예를 들어, Safari 앱을 두 개의 윈도우로 띄워서 하나는 YouTube를 보고 다른 하나는 문서를 작성하는 식으로 말이죠.
이런 상황에서 기존의 AppDelegate 하나로는 여러 개의 윈도우 생명주기를 관리하기가 까다로웠습니다.
그래서 Apple은 이렇게 역할을 분리했어요:
- AppDelegate: 앱 전체의 생명주기 담당
- SceneDelegate: 각각의 윈도우(Scene) 생명주기 담당
이제 하나의 앱에서 여러 윈도우가 독립적으로 작동할 수 있게 된 거죠
이제 좀 더 UIScene에 대해 들어가보겠습니다
UIScene 시스템의 3가지 핵심 구성요소
UIScene 시스템을 이해하려면 이 세 가지 객체가 어떻게 협력하는지 알아야 합니다:
실제 예시로 이해해보겠습니다. iPad에서 Safari 앱으로 검색하면서 유튜브를 보겠습니다:)
1. UIScene = 실제 화면에 보이는 것
- Safari 윈도우 A: YouTube가 열린 Scene 객체
- Safari 윈도우 B: 문서 편집이 열린 Scene 객체
- 각각이 하나의 UIScene 인스턴스가 되는거죠
2. UISceneSession = 화면에 보이는 것의 설계도와 정보 관리
- Safari 윈도우 A의 Session: "YouTube 페이지를 보고 있었음, 전체화면 모드, 볼륨 50%"
- Safari 윈도우 B의 Session: "네이버에서 주식 검색중, 키보드 표시됨, 커서 위치 저장"
Scene이 메모리에서 사라져도 Session은 남아있어서, 나중에 윈도우를 다시 열 때 이전 상태 그대로 복원 가능!
3. UISceneDelegate
- Scene의 생명주기 이벤트를 처리하는 델리게이트
좀 더 이해가 쉽도록 어떻게 과정이 이루어지는 체크해 보겠슴다!
1단계: 사용자가 Safari에서 "새 윈도우" 버튼 터치
- 시스템: “새로운 scene 필요하네!” → UISceneSession 생성 (윈도우를 어떻게 띄울지에 대한 설계도 역할)
2단계: Session 정보를 바탕으로 실제 윈도우 생성
- 시스템: Session 정보 확인 → UIScene 생성 → SceneDelegate 연결 → 화면에 새 Safari 윈도우 등장!
3단계: 사용자가 윈도우 사용하다가 메모리 부족으로 시스템이 윈도우 종료
- 시스템: SceneDelegate에게 "상태 저장해주세요" → Session에 현재 상태 저장 → UIScene 메모리에서 제거
4단계: 나중에 사용자가 다시 해당 윈도우 터치
- 시스템: Session에서 저장된 상태 확인 → 새로운 UIScene 생성 → 이전 상태 그대로 복원! (YouTube 동영상 이어보기 가능)
클래스 계층구조 뜯어보기
UIScene의 상속 구조
코드를 타고 들어가보면 UIScene은 UIResponder를 상속하고 있네요?
그러면 UIResponder는 뭘까요?
우선 터치 이벤트, 모션 이벤트 등 유저가 하는 이벤트들이 발생하면 UIKit은 앱의 응답자 객체들에게 전달하여 처리하도록 합니다.
그 응답자 객체가 바로 UIResponder를 상속하고 있는 객체들인 거죠.
UIResponder를 상속하고 있는 서브클래스들을 보면:
- UIApplication: 앱 자체에서 받고
- UIViewController: 뷰컨트롤러들도 받고
- UIView: 모든 뷰들이 받고
- UIScene: Scene들이 받게 되는거죠
그러면 저걸 상속하게 되면 어떻게 되는가? 라고 물어본다면
1. 터치 이벤트 (Touch Events):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
터치에 관한 메서드들을 사용할 수 있고,
2. 모션 이벤트 (Motion Events): 디바이스 흔들기 등
override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?)
3. 프레스 이벤트 (Press Events): 외부 키보드, 리모컨 등
4. 원격 제어 이벤트 (Remote Control Events): 음악 재생 컨트롤 등
First Responder Chain의 핵심 동작 원리
그러면 이벤트가 발생하면 어디서 처리해야할지는 어떻게 알 수 있을까요?
바로 Responder Chain에 의해 이벤트를 처리할 객체를 찾아가는 구조입니다!
이 중에 First Responder는 이벤트를 가장 먼저 받을 수 있는 응답자를 의미합니다
- 텍스트 필드 탭 -> UITextField가 First Responder
이벤트 전달 흐름
이벤트가 발생하면 다음 순서로 처리됩니다:
- 터치 이벤트의 경우:(iOS 13+기준)
터치된 뷰 → 상위 뷰 → 상위 뷰 → 뷰 컨트롤러 → UIWindow → UIWindowScene → UIApplication → UIApplicationDelegate
- 기타 이벤트의 경우:
First Responder → Next Responder → Next Responder → ... → UIApplication
SceneDelegate가 UIResponder를 상속하는 진짜 이유
자, 이제 궁금증의 핵심에 도달했습니다! Xcode에서 새 프로젝트를 만들면 SceneDelegate가 이렇게 생겼죠:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
// ...
}
iPad에서 같은 앱의 두 개 윈도우가 열려있을 때를 생각해보세요:
- 윈도우 A: 키보드 입력을 받는 중
- 윈도우 B: Apple Pencil 이벤트를 처리하는 중
- 각각 다른 외부 키보드 단축키를 처리해야 하는 상황
이런 경우 Scene별로 독립적인 Responder Chain을 가져야 하니까,
UIScene과 SceneDelegate 모두 UIResponder를 상속하는 구조가 필요한 거였어여
이후의 역할도
'면접준비' 카테고리의 다른 글
근본으로돌아가자(7)-String,Array으로 시작해서 Sequence까지 (0) | 2025.04.04 |
---|---|
Metal(2)-셰이더 코드 작성까지 (0) | 2025.04.02 |
Metal(1)- 메탈을 알기전에 필요한 것들 (1) | 2025.03.29 |
Autolayout 모든 것: 사이클부터 제약조건까지 (0) | 2025.03.26 |
Apple의 보안 (0) | 2025.03.15 |