Upgrade to Pro — share decks privately, control downloads, hide ads and more …

詳解UIWindow

 詳解UIWindow

2024/08/22(木)~24(土)に開催されたiOSDC Japan 2024で発表した2024/08/23 15:40〜 Track A「詳解UIWindow」の資料です

Atsuya Sato

August 23, 2024
Tweet

More Decks by Atsuya Sato

Other Decks in Programming

Transcript

  1. 2 atsuyan / ͋ͭ΍ (𝕏: @n_atmark) ϐΫγϒגࣜձࣾ ৽نࣄۀ෦ iOSΤϯδχΞ 2024೥2݄த్ೖࣾ

    iPad޲͚ϖΠϯτπʔϧPastelaͷ։ൃΛ୲౰ ϘϧμϦϯά͕झຯͰ͢!
  2. 3

  3. UIWindowͷ໾ׂ • ࠲ඪ஋ͷม׵ 10 func convert(_ point: CGPoint, to window:

    UIWindow?) -> CGPoint func convert(_ point: CGPoint, from window: UIWindow?) -> CGPoint func convert(_ rect: CGRect, to window: UIWindow?) -> CGRect func convert(_ rect: CGRect, from window: UIWindow?) -> CGRect
  4. UIWindowͷ໾ׂ • ΩʔϘʔυΠϕϯτͷλʔήοτ؅ཧ 11 var isKeyWindow: Bool { get }

    var canBecomeKey: Bool { get } func makeKeyAndVisible() func makeKey() func becomeKey() func resignKey()
  5. "Apple Studio Display Free Mockups" © BRIX Templates (Licensed under

    CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  6. "Apple Studio Display Free Mockups" © BRIX Templates (Licensed under

    CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  7. αΠζΛͲ͏औಘ͢Δ͔ • αΠζऔಘͷ஫ҙ 31 @available(iOS, introduced: 2.0, deprecated: 13.0, message:ŋŋŋ)

    UIApplication.shared.keyWindow @available(iOS, introduced: 2.0, deprecated: 15.0, message:ŋŋŋ) UIApplication.shared.windows @available(iOS, introduced: 3.2, deprecated: 16.0, message:ŋŋŋ) UIScreen.screens // soft-deprecated @available(iOS, introduced: 2.0, deprecated: 100000, message:ŋŋŋ) UIScreen.main
  8. UIScreen.screens ͲͷΠϯελϯε ͕Ͳͬͪͩ…? iPadଆͷαΠζ͸ҰԠUIScreen.main ͰऔΕ͸͢Δ͕… (soft-deprecated) "Apple Studio Display Free

    Mockups" © BRIX Templates (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  9. UIApplication.shared.connectedScenes ͲͷΠϯελϯε͕ Ͳͷγʔϯͩ…? "Apple Studio Display Free Mockups" © BRIX

    Templates (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  10. UIApplication.shared.windows ͍ͬͺ͍͋Δͧ…? "Apple Studio Display Free Mockups" © BRIX Templates

    (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  11. keyWindowͬͯͲΕͩ??? UIApplication.shared.keyWindow ? ? ? ? "Apple Studio Display Free

    Mockups" © BRIX Templates (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  12. windowScene.windows ͲͷΠϯελϯε ͕Ͳͬͪͩ…? "Apple Studio Display Free Mockups" © BRIX

    Templates (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  13. windowScene.keyWindow keyWindowͬͯͲΕͩ??? ? ? "Apple Studio Display Free Mockups" ©

    BRIX Templates (Licensed under CC BY 4.0) "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  14. view.window @MainActor class UIView : UIResponder { var window: UIWindow?

    { get } } "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  15. @MainActor class UIWindow : UIView { weak var windowScene: UIWindowScene?

    { get set } } window.windowScene "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  16. @MainActor class UIWindowScene : UIScene { var screen: UIScreen {

    get } } windowScene.screen "iPad Pro Mockup" © Eduard (Licensed under CC BY 4.0)
  17. αΠζΛͲ͏औಘ͢Δ͔ 44 UIViewͷํ޲͔Βॱ൪ʹḷΔͷ͕ྑ͍ • UIView͔Βॱ൪ʹḷΔ͜ͱͰରԠ͢Δ্ҐΫϥεΛऔಘͰ͖Δ • UIView 1 — 0…1

    UIWindow • UIWindow 1 — 0..1 UIWindowScene • UIWindowScene 1 — 1 UIScreen (SwiftUIʹ͍ͭͯ͸ޙ΄Ͳ)
  18. UIWindowͷ׆༻ • ཁૉΛॏͶΔ͚ͩͳΒUIViewͷaddSubViewͰ্ʹॏͶͨΓɺ SwiftUIͷ ZStack / overlay ͕͋Δ • ྫ͑͹

    UIViewContoller Λpresentͨ͠ΓSwiftUIͰsheetΛදࣔ͠ ͨΓ͢Δͱɺը໘࠷લ໘Ͱ͸ͳ͘ͳͬͯ͠·͏ • ৗʹը໘࠷લ໘ʹද͍ࣔͨ࣌͠ʹUIWindow͕ศར 48
  19. UIWindowͷදࣔ 50 final class SceneDelegate: UIResponder, UIWindowSceneDelegate{ var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } // ᶃ Πϯελϯεͷੜ੒ let window = UIWindow(windowScene: windowScene) // ᶄ rootViewControllerͷઃఆ window.rootViewController = CustomViewController() // ᶅ UIWindowͷදࣔ window.makeKeyAndVisible() // ᶆ Πϯελϯεͷอ࣋ self.window = window } }
  20. UIWindowΛը໘࠷લ໘ʹදࣔ͢Δʹ͸ • windowLevel ͕ಉ͡UIWindowؒʹ͓͚Δදࣔॱ • ৽͘͠ visible ঢ়ଶʹͳͬͨUIWindow΄Ͳલʹදࣔ͞ΕΔ 57 UIWindow

    ͷ visibility ʹมߋ͕͋ͬͨࡍʹ UIWindow.didBecomeVisibleNotification Ͱߋ৽͞Εͨ UIWindow ͕௨஌͞ΕΔͷͰɺ͜Εͷ௨஌͕࠷ޙʹ͞Εͨ΋ͷ*1 *1: Undocumented ͕ͩɺprivate APIΛίʔϧͯ͠ௐ΂ͯΈͨײ͓ͦ͡Β࣮͘ࡍͷදࣔॱܾఆ͸ UIScene ͕ ಺෦Ͱอ͍࣋ͯ͠Δ NSArray *_visibleWindows ॱʹͳ͍ͬͯͦ͏
  21. 60 UIWindow1 UIWindow2 UIWindow3 "iPad Pro Mockup" © Eduard (Licensed

    under CC BY 4.0) "Mac Keyboard Mockup for Figma" © ͋͛ (Licensed under CC BY 4.0) ? ? ?
  22. 61 UIWindow1 UIWindow2 UIWindow3 "iPad Pro Mockup" © Eduard (Licensed

    under CC BY 4.0) "Mac Keyboard Mockup for Figma" © ͋͛ (Licensed under CC BY 4.0) isKeyWindow: false isKeyWindow: false isKeyWindow: true isKeyWindow: true
  23. ͦͷଞͷؾΛ͚ͭΔ΂͖UIWindowͷಛੑက 67 final class CustomViewController: UIViewController { private var isHidden:

    Bool override var prefersStatusBarHidden: Bool { return isHidden } init(isHidden: Bool, color: UIColor?) { self.isHidden = isHidden super.init(nibName: nil, bundle: nil) view.backgroundColor = color } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
  24. 68 @main class AppDelegate: UIResponder, UIApplicationDelegate { var windows: [UIWindow]

    = [] func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { let blueWindow = UIWindow(frame: UIScreen.main.bounds) blueWindow.rootViewController = CustomViewController(isHidden: false, color: .blue) blueWindow.windowLevel = .init(2) blueWindow.isHidden = false windows.append(blueWindow) let redWindow = UIWindow(frame: UIScreen.main.bounds) // ΑΓલ໘ʹ࠷લ໘දࣔ͞ΕͨblueWindow͕͍ΔͷͰεςʔλεόʔ͸ӅΕͳ͍ redWindow.rootViewController = CustomViewController(isHidden: true, color: .red) redWindow.windowLevel = .init(1) redWindow.isHidden = false windows.append(redWindow) let greenWindow = UIWindow(frame: .init(x: 20, y: 20, width: 100, height: 100)) // ࠷લ໘ʹදࣔ͞Ε͍ͯΔ͕શը໘දࣔ͞Ε͍ͯͳ͍ͷͰεςʔλεόʔ͸ӅΕͳ͍ greenWindow.rootViewController = CustomViewController(isHidden: true, color: .green) greenWindow.windowLevel = .init(3) greenWindow.isHidden = false windows.append(greenWindow) return true } }
  25. 1: App಺ͷView͔Βऔಘ͢Δ৔߹ 72 @main struct SampleAppApp: App { // UIApplicationDelegateAdaptorΛར༻͢Δ

    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } final class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role) if connectingSceneSession.role == .windowApplication { configuration.delegateClass = SceneDelegate.self } return configuration } }
  26. 1: App಺ͷView͔Βऔಘ͢Δ৔߹ 73 final class SceneDelegate: UIResponder, UIWindowSceneDelegate, ObservableObject {

    var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } self.window = windowScene.keyWindow // γʔϯͷ઀ଓ࣌ʹ͸keyWindowΛͱͬͯ໰୊ͳ͍ (UIWindow͕Ұ͚ͭͩͳͷͰ) } }
  27. 1: App಺ͷView͔Βऔಘ͢Δ৔߹ 74 struct ContentView: View { @EnvironmentObject var sceneDelegate:

    SceneDelegate var body: some View { // EnvironmentObject͔Βऔಘͨ͠sceneDelegateΛར༻ͯ͠ sceneDelegate.window͕ར༻Ͱ͖Δ } }
  28. 2: App֎Ͱ࡞੒ͨ͠UIWindowͷ৔߹ 75 extension UIWindow: ObservableObject {} final class SceneDelegate:

    NSObject, UIWindowSceneDelegate{ var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } let window = UIWindow(windowScene: windowScene) // environmentObjectͰUIWindowΛ౉ͯ͋͛͠Δ window.rootViewController = UIHostingController(rootView: OverlayView().ignoresSafeArea().environmentObject(window)) window.isHidden = false self.window = window } }
  29. 2: App֎Ͱ࡞੒ͨ͠UIWindowͷ৔߹ 76 struct OverlayView: View { @EnvironmentObject var window:

    UIWindow var body: some View { // EnvironmentObject͔Βऔಘͨ͠window͕ར༻Ͱ͖Δ } }