-
Haz que tu app UIKit sea más flexible
Descubre cómo tu app UIKit puede volverse más flexible en iPhone, iPad, Mac y Apple Vision Pro mediante el uso de escenas y controladores de vista de contenedor. Aprende a liberar todo el potencial de tu app mediante la transición de un ciclo de vida centrado en la app a uno basado en escenas, que incluye un redimensionamiento de ventana y una multitarea mejorados. Explora las mejoras de UISplitViewController, como el cambio de tamaño de columnas interactivo y soporte de primera clase para columnas de inspector. También haz que tus vistas y controles sean más adaptables con nuevas API de diseño.
Capítulos
- 0:00 - Introducción
- 0:58 - Escenas
- 4:58 - Controladores de vista de contenedor
- 10:45 - Adaptabilidad
- 15:39 - Compatibilidad futura
- 16:07 - Próximos pasos
Recursos
Videos relacionados
WWDC25
WWDC24
-
Buscar este video…
-
-
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 - Introducción
Haz que las apps UIKit sean flexibles y adaptables a diferentes tamaños de pantalla y plataformas al utilizar escenas, controladores de vista de contenedor y otras API.
- 0:58 - Escenas
Las escenas representan instancias distintas de la interfaz de usuario de una app. Cada escena administra su estado de forma independiente y se restaura sin problemas al volver a conectarse. Las escenas proporcionan contexto sobre la visualización de la app, tales como el tamaño de la pantalla y la geometría de la ventana. A partir del próximo lanzamiento importante posterior a iOS 26, la adopción del ciclo de vida UIScene será obligatoria.
- 4:58 - Controladores de vista de contenedor
Los controladores de vista de contenedor como 'UISplitViewController' y 'UITabBarController' administran el diseño de uno o más controladores de vista secundarios. Estos ayudan a que las apps sean flexibles, adaptables y personalizables. Utiliza la API UISceneRestrictions para expresar el tamaño mínimo de las escenas en la app.
- 10:45 - Adaptabilidad
Las guías de diseño y los márgenes ayudan a posicionar el contenido de una app de manera consistente dentro del área segura del dispositivo. iPadOS 26 introduce un nuevo control de ventana. Las apps pueden especificar 'preferredWindowingControlStyle' y guías de diseño para acomodar estos controles. Las interfaces de usuario adaptables deben responder rápidamente a los cambios de tamaño y orientación, pero algunas apps pueden querer anular 'prefersInterfaceOrientationLocked' para bloquear temporalmente la orientación. Para operaciones computacionalmente costosas, marca 'isInteractivelyResizing' para realizar las operaciones después de que finalice una interacción. 'UIRequiresFullscreen' está obsoleto.
- 15:39 - Compatibilidad futura
Con el SDK de iOS 26, las apps se adaptan automáticamente a nuevos tamaños de pantalla sin necesidad de actualizaciones manuales ni reenvíos.
- 16:07 - Próximos pasos
Para crear una app flexible, adopta el ciclo de vida de la escena, utiliza controladores de vista de contenedor y aprovecha las API como guías de diseño.