
-
UIKit 앱을 더욱 유연하게 만들기
iPhone, iPad, Mac 및 Apple Vision Pro에서 장면 및 컨테이너 뷰 컨트롤러를 사용하여 UIKit 앱을 더욱 유연하게 만드는 방법을 알아보세요. 앱 중심의 수명 주기에서 장면 기반의 수명 주기로 전환하여 향상된 윈도우 크기 조절 및 개선된 멀티태스킹 등 앱의 잠재력을 최대한 발휘할 수 있습니다. 상호작용 방식의 열 크기 조정 및 Inspetcor 열의 온전한 지원 등 UISplitViewController의 향상된 기능을 살펴보세요. 새로운 레이아웃 API를 채택하여 뷰와 제어의 적응력을 더욱 높일 수도 있습니다.
챕터
리소스
관련 비디오
WWDC25
WWDC24
-
비디오 검색…
“UIKit 앱을 더욱 유연하게 만들기”에 오신 것을 환영합니다 저는 UIKit 팀 엔지니어 Alexander MacLeod입니다 유연한 앱은 다양한 크기와 플랫폼으로 놀라운 경험을 선사합니다 어떤 크기에서도 익숙하고 직관적인 탐색 환경을 유지하죠 이 비디오에서는 앱의 유연성을 유지하는 모범 사례를 설명합니다 먼저 장면의 기본 사항을 살펴보고 이것이 유연한 앱의 기초인 이유를 안내합니다 다음으로 컨테이너 보기 컨트롤러를 다룹니다 UISplitViewController UITabBarController 등인데 이것으로 앱의 유연성을 높이는 방법을 살펴봅니다 마지막으로 진정 유연한 적응형 UI를 빌드하는 데 도움이 되는 API를 설명합니다 먼저 장면부터 보죠 장면이란 앱 UI의 인스턴스입니다 앱의 보기 컨트롤러와 보기가 포함되어 있고요 장면은 앱의 UI에 있는 섹션에 딥링크하기 위한 URL 등 외부 데이터를 처리하기 위한 후크를 제공합니다
각 장면은 독립적으로 UI 상태를 저장하고 복원합니다 장면은 디스크에 유지하기에 앞서 현재 상태를 묻기 가장 좋은 기회를 결정합니다 장면이 다시 연결되면 이전 UI 상태를 쿼리할 수 있습니다 이를 통해 장면을 이전과 동일하게 복원할 수 있습니다 장면은 또한 화면의 세부 정보 창의 지오메트리 등 앱이 표시되는 방식에 대한 컨텍스트를 제공합니다 장면은 여러 개일 수 있고 각기 자체 라이프 사이클, 상태가 있죠 전용 장면 유형은 독특한 경험을 담도록 디자인되었습니다 메시지 앱이라면 새 메시지 전송을 위한 전용 구성 장면이 그 예인데 이제 iOS 26 기준 단일 앱에서 SwiftUI UIKit 장면 유형을 혼합할 수 있죠 자세한 내용은 “UIKit의 새로운 기능”을 참고하세요 장면을 통한 이동성은 유연한 앱의 완벽한 기반입니다
장면은 유연성에 필수적이므로 UIScene 라이프 사이클 채택이 곧 필수가 될 것입니다 iOS 26 이후 다음번 주요 릴리스에서는 최신 SDK로 빌드할 때 UIScene 라이프 사이클이 필요합니다
여러 장면을 지원하는 것이 권장되지만 장면 라이프 사이클 채택만 필수 사항입니다 UIScene 라이프 사이클을 채택하는 방법에 대한 내용은 기술 메모 “UIKit 장면 기반 라이프 사이클로 마이그레이션하기”를 확인하세요 장면은 매우 중요하기 때문에 실제 예를 보여 드리겠습니다 저는 특정 작업에 소요되는 시간을 추적하는 앱을 개발했는데요 AirPlay로 현재 작업을 Apple TV로 전송하는 기능이 있습니다 연결 세션의 장면 구성을 판단하는 것은 앱 대리자가 담당합니다 configurationForConnecingSceneSession 대리자 메서드에서 장면 세션의 역할을 확인하고요 역할이 비대화형 외부 디스플레이면 맞춤형 장면 구성을 반환합니다 이외에는 기본 장면 구성이 선호됩니다 각 구성은 앱의 Info.plist 파일에 정의되어 있습니다
UISceneDelegate는 개별 장면의 라이프 사이클을 관리합니다 sceneWillConnectToSession에서 먼저 창을 만들고 연결 장면과 연결합니다 장면 구성에서 스토리보드를 지정하면 창이 자동으로 생성됩니다
창의 루트 보기 컨트롤러 지정 후 타이머 모델 등 장면별 데이터를 제공합니다 이 앱에서는 장면이 백그라운드로 이동할 때 타이머를 일시 정지해야 하는데 sceneDidEnterBackground 대리자 메서드를 구현해 타이머를 일시 정지합니다 연결 장면의 UI 상태가 이전과 동일하게 유지되도록 상태 복원을 처리합니다 장면 대리자는 상태 복원 활동을 제공하며 여기에는 선택, 탐색 경로 기타 UI 상태가 포함될 수 있죠 시스템은 이 UI 상태를 유지하고 장면 인스턴스와 연결합니다 나중에 장면이 다시 연결되면 restoreInteractionStateWith userActivity 대리자 메서드에서 상태 복원 활동이 사용 가능해집니다 사용자 활동의 정보로 타이머 모델을 채우면 연결 장면의 UI 상태가 이전과 동일하게 유지됩니다
UIScene 라이프 사이클을 채택해 유연한 앱을 위한 강력한 기반이 마련되었습니다 이제 컨테이너 보기 컨트롤러를 살펴보고 이것이 유연한 앱을 빌드하는 데 중요한 이유를 설명하겠습니다 컨테이너 보기 컨트롤러는 하나 이상 하위 항목 보기 컨트롤러의 레이아웃을 관리하는 역할을 하죠 UIKit은 유연하게 디자인된 컨테이너 보기 컨트롤러를 다수 제공합니다 먼저 UISplitViewController를 보겠습니다 UISplitViewController는 여러 인접 콘텐츠 열의 표시를 관리해 정보 계층 구조 전반에 걸쳐 원활한 탐색을 지원합니다 수평 공간이 한정적이면 Split View 컨트롤러가 열을 탐색 스택으로 축소해 조정됩니다 UISplitViewController에는 대화형 열 크기 조정 등 각종 새로운 기능이 추가되었습니다 Split View 컨트롤러의 구분 표시를 드래그해 열 크기를 조정하며 포인터를 사용할 때는 포인터 모양이 바뀌어 열의 크기를 조정할 수 있는 방향을 나타냅니다 UISplitViewController는 열별로 기본 최소, 최대, 선호 너비를 제공합니다 앱에는 더 넓은 너비로 콘텐츠를 표시하는 것을 선호하는 열이나 기본 너비의 일부만으로도 기능이 유지되는 열이 있습니다 각 열의 최소, 최대, 선호 너비는 연결된 Split View 컨트롤러 속성을 사용해 맞춤화할 수 있습니다 표시할 수 있는 열 수가 제한되는 너비를 요구해서는 안 됩니다 앱의 유연성이 떨어지기 때문입니다 Split View 컨트롤러의 확장 또는 축소 여부에 따라 UI를 조정해야 할 수도 있는데요 Mail에서는 Split View 컨트롤러가 축소되었을 때 공개 표시기가 표시되어 셀 선택 시 추가 내용을 드러낼 수 있음을 알립니다 새로운 특성인 Split View 컨트롤러 레이아웃 환경은 상위 Split View 컨트롤러의 확장 또는 축소 여부를 알립니다 이 예에서는 Split View 컨트롤러가 축소되면 해당 특성을 쿼리하여 공개 표시기를 조건부 추가합니다 또 하나 새로운 사항은 인스펙터 열에 대한 최고 수준의 지원입니다 인스펙터란 Split View 컨트롤러의 열로서 선택한 콘텐츠의 추가 세부 정보를 제공하는 역할을 합니다 미리보기는 인스펙터를 사용해 사진과 함께 메타데이터를 보조 열에 표시합니다 Split View 컨트롤러를 확장했을 때는 인스펙터 열이 보조 열에 인접한 끝 방향 가장자리에 위치합니다
축소 시에는 Split View 컨트롤러가 자동으로 조정되어 인스펙터 열을 시트로 표시합니다
Split View 컨트롤러에 인스펙터를 통합하려면 인스펙터 열을 위한 보기 컨트롤러를 지정합니다 Split View 컨트롤러가 처음 나타나면 인스펙터 열이 숨겨지죠 인스펙터 열을 표시하려면 show를 호출합니다 UISplitViewController는 유연하게 디자인되었으며 앱이 어떤 크기에서나 최고의 탐색 경험을 제공하도록 합니다 또 다른 컨테이너는 UITabBarController입니다 UITabBarController는 한 영역에 상호 배타적인 콘텐츠 패널 여러 개를 표시합니다 탭 막대로 각 패널의 현재 상태를 유지하면서 탭 간에 빠르게 전환할 수 있습니다 나아가 탭 막대의 모양은 각 플랫폼에 맞게 조정됩니다
iPhone에서는 탭 막대가 장면 하단에 있습니다 Mac에서는 탭 막대가 도구 막대에 있거나 사이드바로 표시됩니다
Apple Vision Pro에서는 탭 막대가 화면의 시작 방향 가장자리 오너먼트에 표시됩니다 iPad에서는 탭 막대가 장면 상단에 탐색 컨트롤과 함께 있습니다 탭 막대를 사이드바로 변형해 콘텐츠 모음에 빠르게 접근할 수도 있습니다 탭 그룹은 사이드바에 추가 대상을 표시합니다 iPad의 음악 앱을 보면 Library(라이브러리) 탭 그룹에 Artists(아티스트) Albums(앨범) 등이 있습니다
사이드바가 없으면 Library (라이브러리) 그룹이 탭 대상이죠
UITabBarController는 이 조정을 원활하게 관리할 API를 제공합니다 먼저 탭 그룹에 관리 탐색 컨트롤러를 제공합니다 탭 그룹의 리프 탭을 선택하면 해당 보기 컨트롤러는 상위 그룹의 보기 컨트롤러와 함께 탐색 스택에 푸시됩니다 이 탐색 스택에 푸시된 보기 컨트롤러를 맞춤화하려면 UITabBarController 대리자 메서드를 구현합니다 displayedViewControllersFor 탭이죠 이 예에서 라이브러리 탭을 선택할 수 없으면 대리자 메서드는 빈 배열을 반환해 스택에서 라이브러리 탭의 보기 컨트롤러를 생략합니다
UITabBarController가 탭 막대 또는 사이드바 표시의 유연성을 향상하는 방법에 대한 내용은 WWDC24 “iPadOS에서 탭 및 사이드바 경험 향상하기”를 시청하세요 UISplitViewController UITabBarController 같은 컨테이너 보기 컨트롤러 채택이 앱 유연성 향상에 가장 좋습니다 이러한 컨테이너는 다양한 크기를 지원하도록 디자인되었지만 앱에는 핵심 기능을 유지하기 위한 최소 크기가 필요할 수 있습니다 UISceneSizeRestrictions API를 사용하면 장면 콘텐츠의 선호 최소 크기를 표현할 수 있습니다 최소 크기를 지정하는 가장 좋은 시점은 장면이 연결되기 직전이죠 이 예에서는 최소 너비를 500포인트로 지정합니다 진정 유연한 앱이라면 자체 UI도 조정 가능해야 합니다 다음으로 적응형 UI를 빌드할 수 있는 API를 설명하겠습니다 적응형 UI를 만들 때는 콘텐츠가 안전 영역 내에 유지되도록 하는 단계가 중요하죠 안전 영역은 보기에서 대화형 콘텐츠 또는 중요 콘텐츠에 적합한 영역입니다 이 영역 외부에 배치된 콘텐츠는 탐색 막대, 도구 막대 등에 덮일 위험이 있습니다
상태 막대 등 시스템 UI나 Dynamic Island 등 기기 기능에 콘텐츠가 가려질 수도 있습니다
사이드바는 Split View 컨트롤러의 인접 열에 비대칭 안전 영역 삽입 부분을 추가합니다 백그라운드는 안전 영역 바깥 사이드바 아래로 확장 가능합니다
메시지 내용 등의 콘텐츠는 안전 영역 내에 배치되어 계속 보이도록 합니다 메시지 말풍선은 여백을 사용해 레이아웃 안전 영역의 가장자리에서 삽입됩니다 그래서 일정한 간격이 확보되고 사이드바와 시각적으로 분리됩니다
보기별로 콘텐츠 주위 표준 여백을 적용하는 레이아웃 가이드가 있죠 레이아웃 여백은 기본적으로 안전 영역에서 삽입됩니다 이 예에서는 컨테이너 보기 내부의 콘텐츠를 배치하는 레이아웃 가이드를 요청합니다 이 레이아웃 가이드로 콘텐츠 보기의 제약 조건을 구성합니다
iPadOS 26에서 장면에는 macOS와 유사하게 창을 닫고 최소화하고 배열하는 새로운 컨트롤이 있습니다 이 창 컨트롤은 장면의 콘텐츠와 함께 나타납니다
장면은 콘텐츠 보완을 위해 선호 창 제어 스타일을 지정할 수 있죠 선호 사항을 지정하려면 장면에 UIWindowSceneDelegate 메서드 preferredWindowingControlStyle을 구현합니다
UINavigationBar 같은 시스템 컴포넌트는 창 컨트롤 주변의 하위 보기를 배열해 자동으로 조정됩니다 UI도 스타일과 관계없이 창 컨트롤에 맞게 조정되어야 합니다
UI가 가려지지 않도록 하려면 창 컨트롤을 고려한 레이아웃 가이드를 사용합니다 여기서는 수평 모서리 적응을 통한 레이아웃 여백 가이드를 요청하죠 이 레이아웃 가이드는 장면 상단의 막대형 콘텐츠에 적합합니다 창 컨트롤의 끝 방향 가장자리에서 안쪽으로 삽입해야 하는 경우예요 이 레이아웃 가이드로 콘텐츠 보기의 제약 조건을 구성합니다 적응형 UI라면 인터페이스 방향이 중복되어야 합니다 장면 크기 조정, 기기 회전 창 레이아웃 변경 시 모두 궁극적으로 장면 크기에 맞게 수정됩니다 특정 앱 카테고리에서는 일시적 방향 고정이 유용할 수 있습니다 예를 들어 운전 게임에서 차량을 조작하느라 기기를 회전해야 할 때 방향을 고정시켜야 할 수 있습니다
보기 컨트롤러가 표시 중일 때 고정된 인터페이스 방향을 선호할 수 있는데 선호 사항을 지정하려면 보기 컨트롤러 하위 클래스에서 prefersInterfaceOrientationLocked를 재정의합니다 이 선호 사항이 변경될 때마다 setNeedsUpdateOfPrefersInterfaceOrientationLocked를 호출합니다
인터페이스 방향 잠금 관찰은 UIWindowSceneDelegate 메서드 didUpdateEffectiveGeometry를 구현하면 됩니다 isInterfaceOrientationLocked 값 변경 여부를 이후 비교합니다 진정한 적응형 앱이라면 크기 조정에 빠르게 반응해야 합니다 앱의 UI에는 그리기에 계산 비용이 많이 드는 요소가 있을 수 있죠
장면의 크기가 변경되면 많은 애셋의 크기도 조정해야 하는 게임에서 흔한 경우입니다 크기 조정 상호작용의 크기별로 애셋을 다시 렌더링해야 합니다 이 예에서는 isInteractivelyResizing을 쿼리해 상호작용 완료 후 새 장면 크기를 위한 애셋만 업데이트합니다
유연한 앱은 사용자가 원하는 대로 기기를 사용할 수 있는 앱입니다 다양한 크기에서 우수한 경험을 선사해 방향이나 레이아웃의 구애 없이 사용할 수 있죠 UIRequiresFullscreen Info.plist 키는 iOS 9의 호환 모드로서 장면 크기 조정을 방지하는데요 UIRequiresFullscreen은 지원이 중단되어 향후 무시됩니다 적응형 조정 앱은 이 키를 필요로 하지 않으며 제거해야 합니다
특히 새 하드웨어를 위한 다른 호환성 모드가 있습니다 이전에는 다른 화면 크기로 새 하드웨어가 출시되었을 때 시스템에서 앱의 UI를 크기 조절하거나 레터박스 처리했습니다 해당 크기 조절은 앱을 최신 SDK로 빌드해 다시 제출할 때까지 유지됩니다 iOS 26 SDK로 빌드하고 제출한 후에는 앱 UI가 더 이상 새 화면 크기에 맞게 크기가 조절되거나 레터박스 처리되지 않습니다
이렇게 앱의 유연성을 유지하는 모범 사례를 살펴보았습니다 다음으로 무엇을 해야 할까요? 앱에 장면 라이프 사이클을 채택해 유연한 앱을 위한 강력한 기반을 마련하세요 컨테이너 보기 컨트롤러를 사용해 UI 컴포넌트를 관리하세요 또한 레이아웃 가이드 같은 API는 적응형 UI 빌드에 유용합니다 여러분이 만드실 더 유연한 앱이 기대됩니다 감사합니다!
-
-
3:02 - Specify the scene configuration
// Specify the scene configuration @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting sceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { if sceneSession.role == .windowExternalDisplayNonInteractive { return UISceneConfiguration(name: "Timer Scene", sessionRole: sceneSession.role) } else { return UISceneConfiguration(name: "Main Scene", sessionRole: sceneSession.role) } } }
-
3:30 - Configure the UI
// Configure the UI class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var timerModel = TimerModel() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let windowScene = scene as! UIWindowScene let window = UIWindow(windowScene: windowScene) window.rootViewController = TimerViewController(model: timerModel) window.makeKeyAndVisible() self.window = window } }
-
3:56 - Handle life cycle events
// Handle life cycle events class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var timerModel = TimerModel() // ... func sceneDidEnterBackground(_ scene: UIScene) { timerModel.pause() } }
-
4:09 - Restore UI state
// Restore UI state class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? var timerModel = TimerModel() // ... func stateRestorationActivity(for scene: UIScene) -> NSUserActivity? { let userActivity = NSUserActivity(activityType: "com.example.timer.ui-state") userActivity.userInfo = ["selectedTimeFormat": timerModel.selectedTimeFormat] return userActivity } func scene(_ scene: UIScene restoreInteractionStateWith userActivity: NSUserActivity) { if let selectedTimeFormat = userActivity?["selectedTimeFormat"] as? String { timerModel.selectedTimeFormat = selectedTimeFormat } }
-
4:46 - Adapt for the split view controller layout environment
// Adapt for the split view controller layout environment override func updateConfiguration(using state: UICellConfigurationState) { // ... if state.traitCollection.splitViewControllerLayoutEnvironment == .collapsed { accessories = [.disclosureIndicator()] } else { accessories = [] } }
-
6:11 - Customize the minimum, maximum, and preferred column widths
// Customize the minimum, maximum, and preferred column widths let splitViewController = // ... splitViewController.minimumPrimaryColumnWidth = 200.0 splitViewController.maximumPrimaryColumnWidth = 400.0 splitViewController.preferredSupplementaryColumnWidth = 500.0
-
7:37 - Show an inspector column
// Show an inspector column let splitViewController = // ... splitViewController.setViewController(inspectorViewController, for: .inspector) splitViewController.show(.inspector)
-
9:19 - Managing tab groups
// Managing tab groups let group = UITabGroup(title: "Library", ...) group.managingNavigationController = UINavigationController() // ... // MARK: - UITabBarControllerDelegate func tabBarController( _ tabBarController: UITabBarController, displayedViewControllersFor tab: UITab, proposedViewControllers: [UIViewController]) -> [UIViewController] { if tab.identifier == "Library" && !self.allowsSelectingLibraryTab { return [] } else { return proposedViewControllers } }
-
10:25 - Preferred minimum size
// Specify a preferred minimum size class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let windowScene = scene as! UIWindowScene windowScene.sizeRestrictions?.minimumSize.width = 500.0 } }
-
11:57 - Position content using the layout margins guide
// Position content using the layout margins guide let containerView = // ... let contentView = // ... let contentGuide = containerView.layoutMarginsGuide NSLayoutConstraint.activate([ contentView.topAnchor.constraint(equalTo: contentGuide.topAnchor), contentView.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor), contentView.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor) contentView.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor) ])
-
12:34 - Specify the window control style
// Specify the window control style class SceneDelegate: UIResponder, UIWindowSceneDelegate { func preferredWindowingControlStyle( for scene: UIWindowScene) -> UIWindowScene.WindowingControlStyle { return .unified } }
-
13:04 - Respect the window control area
// Respect the window control area let containerView = // ... let contentView = // ... let contentGuide = containerView.layoutGuide(for: .margins(cornerAdaptation: .horizontal) NSLayoutConstraint.activate([ contentView.topAnchor.constraint(equalTo: contentGuide.topAnchor), contentView.leadingAnchor.constraint(equalTo: contentGuide.leadingAnchor), contentView.bottomAnchor.constraint(equalTo: contentGuide.bottomAnchor), contentView.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor) ])
-
13:57 - Request orientation lock
// Request orientation lock class RaceViewController: UIViewController { override var prefersInterfaceOrientationLocked: Bool { return isDriving } // ... var isDriving: Bool = false { didSet { if isDriving != oldValue { setNeedsUpdateOfPrefersInterfaceOrientationLocked() } } } }
-
14:18 - Observe the interface orientation lock
// Observe the interface orientation lock class SceneDelegate: UIResponder, UIWindowSceneDelegate { var game = Game() func windowScene( _ windowScene: UIWindowScene, didUpdateEffectiveGeometry previousGeometry: UIWindowScene.Geometry) { let wasLocked = previousGeometry.isInterfaceOrientationLocked let isLocked = windowScene.effectiveGeometry.isInterfaceOrientationLocked if wasLocked != isLocked { game.pauseIfNeeded(isInterfaceOrientationLocked: isLocked) } } }
-
14:44 - Query whether the scene is resizing
// Query whether the scene is resizing class SceneDelegate: UIResponder, UIWindowSceneDelegate { var gameAssetManager = GameAssetManager() var previousSceneSize = CGSize.zero func windowScene( _ windowScene: UIWindowScene, didUpdateEffectiveGeometry previousGeometry: UIWindowScene.Geometry) { let geometry = windowScene.effectiveGeometry let sceneSize = geometry.coordinateSpace.bounds.size if !geometry.isInteractivelyResizing && sceneSize != previousSceneSize { previousSceneSize = sceneSize gameAssetManager.updateAssets(sceneSize: sceneSize) } } }
-
-
- 0:00 - 서론
다양한 크기의 화면과 플랫폼에서 장면, 컨테이너 뷰 컨트롤러, 다른 API를 사용하여 UIKit 앱을 더욱 유연하게 만드는 방법을 알아보세요.
- 0:58 - 장면
장면은 앱 UI의 개별 인스턴스를 나타냅니다. 각 장면은 독립적으로 상태를 관리하며, 다시 연결되면 원활하게 복원됩니다. 장면은 화면 크기, 윈도우 지오메트리와 같은 앱 디스플레이에 관한 컨텍스트를 제공합니다. iOS 26 이후의 다음 주요 릴리즈부터 UIScene 라이프 사이클의 채택이 필수가 됩니다.
- 4:58 - 컨테이너 뷰 컨트롤러
‘UISplitViewController’, ‘UITabBarController’와 같은 컨테이너 뷰 컨트롤러는 하나 이상의 하위 뷰 컨트롤러의 레이아웃을 관리합니다. 이러한 컨트롤러는 유연하고, 맞춤화가 가능한 적응형 앱을 만드는 데 도움이 됩니다. UISceneRestrictions API를 사용하여 앱의 장면에 대한 최소 크기를 표현하세요.
- 10:45 - 적응성
레이아웃 가이드와 여백은 앱의 콘텐츠를 기기의 안전 영역 내에서 일관되게 배치하는 데 도움이 됩니다. iPadOS 26에서는 새로운 윈도우 컨트롤이 도입되었습니다. 앱은 이러한 컨트롤을 수용하기 위해 ‘preferredWindowingControlStyle’과 레이아웃 가이드를 지정할 수 있습니다. 적응형 UI는 크기 조절 및 방향 변경에 신속하게 대응해야 하지만, 특정 앱에서는 ‘prefersInterfaceOrientationLocked’를 재정의하여 방향을 일시적으로 잠그고 싶어 할 수 있습니다. 계산량이 많은 작업의 경우, 상호작용이 완료된 후에 작업을 실행하려면 ‘isInteractivelyResizing’을 선택하세요. ‘UIRequiresFullscreen’은 지원 중단되었습니다.
- 15:39 - 향후 호환성
iOS 26 SDK를 사용하면 앱을 수동으로 업데이트하거나 다시 제출하지 않아도 앱이 자동으로 새 화면 크기에 맞춰 조정됩니다.
- 16:07 - 다음 단계
유연한 앱을 빌드하려면 장면 라이프 사이클을 채택하고, 컨테이너 뷰 컨트롤러를 사용하고, 레이아웃 가이드와 같은 API를 활용하세요.