From a127bd6f6684909852000cdf32469bac3b611887 Mon Sep 17 00:00:00 2001
From: Mads Marquart <mads@marquart.dk>
Date: Thu, 22 Feb 2024 22:28:49 +0100
Subject: [PATCH] iOS: Split classes in `view.rs` into separate files (#3511)

* Move application delegate to its own file
* Move window subclass to window.rs
* Split view controller to separate file
---
 src/platform_impl/ios/app_delegate.rs    | 103 +++++++
 src/platform_impl/ios/app_state.rs       |   2 +-
 src/platform_impl/ios/event_loop.rs      |   7 +-
 src/platform_impl/ios/mod.rs             |   2 +
 src/platform_impl/ios/view.rs            | 355 +----------------------
 src/platform_impl/ios/view_controller.rs | 183 ++++++++++++
 src/platform_impl/ios/window.rs          |  86 +++++-
 7 files changed, 382 insertions(+), 356 deletions(-)
 create mode 100644 src/platform_impl/ios/app_delegate.rs
 create mode 100644 src/platform_impl/ios/view_controller.rs

diff --git a/src/platform_impl/ios/app_delegate.rs b/src/platform_impl/ios/app_delegate.rs
new file mode 100644
index 0000000000..eb93cdf34c
--- /dev/null
+++ b/src/platform_impl/ios/app_delegate.rs
@@ -0,0 +1,103 @@
+use icrate::Foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
+use objc2::{declare_class, mutability, ClassType, DeclaredClass};
+
+use super::app_state::{self, EventWrapper};
+use super::uikit::{UIApplication, UIWindow};
+use super::window::WinitUIWindow;
+use crate::{
+    event::{Event, WindowEvent},
+    window::WindowId as RootWindowId,
+};
+
+declare_class!(
+    pub struct AppDelegate;
+
+    unsafe impl ClassType for AppDelegate {
+        type Super = NSObject;
+        type Mutability = mutability::InteriorMutable;
+        const NAME: &'static str = "WinitApplicationDelegate";
+    }
+
+    impl DeclaredClass for AppDelegate {}
+
+    // UIApplicationDelegate protocol
+    unsafe impl AppDelegate {
+        #[method(application:didFinishLaunchingWithOptions:)]
+        fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
+            app_state::did_finish_launching(MainThreadMarker::new().unwrap());
+            true
+        }
+
+        #[method(applicationDidBecomeActive:)]
+        fn did_become_active(&self, _application: &UIApplication) {
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
+        }
+
+        #[method(applicationWillResignActive:)]
+        fn will_resign_active(&self, _application: &UIApplication) {
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
+        }
+
+        #[method(applicationWillEnterForeground:)]
+        fn will_enter_foreground(&self, application: &UIApplication) {
+            self.send_occluded_event_for_all_windows(application, false);
+        }
+
+        #[method(applicationDidEnterBackground:)]
+        fn did_enter_background(&self, application: &UIApplication) {
+            self.send_occluded_event_for_all_windows(application, true);
+        }
+
+        #[method(applicationWillTerminate:)]
+        fn will_terminate(&self, application: &UIApplication) {
+            let mut events = Vec::new();
+            for window in application.windows().iter() {
+                if window.is_kind_of::<WinitUIWindow>() {
+                    // SAFETY: We just checked that the window is a `winit` window
+                    let window = unsafe {
+                        let ptr: *const UIWindow = window;
+                        let ptr: *const WinitUIWindow = ptr.cast();
+                        &*ptr
+                    };
+                    events.push(EventWrapper::StaticEvent(Event::WindowEvent {
+                        window_id: RootWindowId(window.id()),
+                        event: WindowEvent::Destroyed,
+                    }));
+                }
+            }
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_events(mtm, events);
+            app_state::terminated(mtm);
+        }
+
+        #[method(applicationDidReceiveMemoryWarning:)]
+        fn did_receive_memory_warning(&self, _application: &UIApplication) {
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
+        }
+    }
+);
+
+impl AppDelegate {
+    fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
+        let mut events = Vec::new();
+        for window in application.windows().iter() {
+            if window.is_kind_of::<WinitUIWindow>() {
+                // SAFETY: We just checked that the window is a `winit` window
+                let window = unsafe {
+                    let ptr: *const UIWindow = window;
+                    let ptr: *const WinitUIWindow = ptr.cast();
+                    &*ptr
+                };
+                events.push(EventWrapper::StaticEvent(Event::WindowEvent {
+                    window_id: RootWindowId(window.id()),
+                    event: WindowEvent::Occluded(occluded),
+                }));
+            }
+        }
+        let mtm = MainThreadMarker::new().unwrap();
+        app_state::handle_nonuser_events(mtm, events);
+    }
+}
diff --git a/src/platform_impl/ios/app_state.rs b/src/platform_impl/ios/app_state.rs
index 49492617b2..d0105dd8a8 100644
--- a/src/platform_impl/ios/app_state.rs
+++ b/src/platform_impl/ios/app_state.rs
@@ -25,7 +25,7 @@ use objc2::{msg_send, sel};
 use once_cell::sync::Lazy;
 
 use super::uikit::UIView;
-use super::view::WinitUIWindow;
+use super::window::WinitUIWindow;
 use crate::{
     dpi::PhysicalSize,
     event::{Event, InnerSizeWriter, StartCause, WindowEvent},
diff --git a/src/platform_impl/ios/event_loop.rs b/src/platform_impl/ios/event_loop.rs
index da3a918104..a0ef0e0f55 100644
--- a/src/platform_impl/ios/event_loop.rs
+++ b/src/platform_impl/ios/event_loop.rs
@@ -27,7 +27,8 @@ use crate::{
     window::{CustomCursor, CustomCursorSource},
 };
 
-use super::{app_state, monitor, view, MonitorHandle};
+use super::app_delegate::AppDelegate;
+use super::{app_state, monitor, MonitorHandle};
 use super::{
     app_state::AppState,
     uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen},
@@ -201,14 +202,14 @@ impl<T: 'static> EventLoop<T> {
         app_state::will_launch(self.mtm, handler);
 
         // Ensure application delegate is initialized
-        view::WinitApplicationDelegate::class();
+        let _ = AppDelegate::class();
 
         unsafe {
             UIApplicationMain(
                 0,
                 ptr::null(),
                 None,
-                Some(&NSString::from_str("WinitApplicationDelegate")),
+                Some(&NSString::from_str(AppDelegate::NAME)),
             )
         };
         unreachable!()
diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs
index 1a38df0e33..496ad35967 100644
--- a/src/platform_impl/ios/mod.rs
+++ b/src/platform_impl/ios/mod.rs
@@ -58,12 +58,14 @@
 #![cfg(ios_platform)]
 #![allow(clippy::let_unit_value)]
 
+mod app_delegate;
 mod app_state;
 mod event_loop;
 mod ffi;
 mod monitor;
 mod uikit;
 mod view;
+mod view_controller;
 mod window;
 
 use std::fmt;
diff --git a/src/platform_impl/ios/view.rs b/src/platform_impl/ios/view.rs
index 0f6a677799..3f3fa8b6cb 100644
--- a/src/platform_impl/ios/view.rs
+++ b/src/platform_impl/ios/view.rs
@@ -1,7 +1,7 @@
 #![allow(clippy::unnecessary_cast)]
-use std::cell::{Cell, RefCell};
+use std::cell::RefCell;
 
-use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSObjectProtocol, NSSet};
+use icrate::Foundation::{CGFloat, CGRect, MainThreadMarker, NSObject, NSSet};
 use objc2::rc::Id;
 use objc2::runtime::AnyClass;
 use objc2::{
@@ -10,20 +10,15 @@ use objc2::{
 
 use super::app_state::{self, EventWrapper};
 use super::uikit::{
-    UIApplication, UIDevice, UIEvent, UIForceTouchCapability, UIGestureRecognizerState,
-    UIInterfaceOrientationMask, UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer,
-    UIStatusBarStyle, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
-    UITraitCollection, UIView, UIViewController, UIWindow,
+    UIEvent, UIForceTouchCapability, UIGestureRecognizerState, UIPinchGestureRecognizer,
+    UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase,
+    UITouchType, UITraitCollection, UIView,
 };
-use super::window::WindowId;
+use super::window::WinitUIWindow;
 use crate::{
     dpi::PhysicalPosition,
     event::{Event, Force, Touch, TouchPhase, WindowEvent},
-    platform::ios::ValidOrientations,
-    platform_impl::platform::{
-        ffi::{UIRectEdge, UIUserInterfaceIdiom},
-        Fullscreen, DEVICE_ID,
-    },
+    platform_impl::platform::DEVICE_ID,
     window::{WindowAttributes, WindowId as RootWindowId},
 };
 
@@ -394,339 +389,3 @@ impl WinitView {
         app_state::handle_nonuser_events(mtm, touch_events);
     }
 }
-
-pub struct ViewControllerState {
-    prefers_status_bar_hidden: Cell<bool>,
-    preferred_status_bar_style: Cell<UIStatusBarStyle>,
-    prefers_home_indicator_auto_hidden: Cell<bool>,
-    supported_orientations: Cell<UIInterfaceOrientationMask>,
-    preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
-}
-
-declare_class!(
-    pub(crate) struct WinitViewController;
-
-    unsafe impl ClassType for WinitViewController {
-        #[inherits(UIResponder, NSObject)]
-        type Super = UIViewController;
-        type Mutability = mutability::InteriorMutable;
-        const NAME: &'static str = "WinitUIViewController";
-    }
-
-    impl DeclaredClass for WinitViewController {
-        type Ivars = ViewControllerState;
-    }
-
-    unsafe impl WinitViewController {
-        #[method(shouldAutorotate)]
-        fn should_autorotate(&self) -> bool {
-            true
-        }
-
-        #[method(prefersStatusBarHidden)]
-        fn prefers_status_bar_hidden(&self) -> bool {
-            self.ivars().prefers_status_bar_hidden.get()
-        }
-
-        #[method(preferredStatusBarStyle)]
-        fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
-            self.ivars().preferred_status_bar_style.get()
-        }
-
-        #[method(prefersHomeIndicatorAutoHidden)]
-        fn prefers_home_indicator_auto_hidden(&self) -> bool {
-            self.ivars().prefers_home_indicator_auto_hidden.get()
-        }
-
-        #[method(supportedInterfaceOrientations)]
-        fn supported_orientations(&self) -> UIInterfaceOrientationMask {
-            self.ivars().supported_orientations.get()
-        }
-
-        #[method(preferredScreenEdgesDeferringSystemGestures)]
-        fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
-            self.ivars()
-                .preferred_screen_edges_deferring_system_gestures
-                .get()
-        }
-    }
-);
-
-impl WinitViewController {
-    pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
-        self.ivars().prefers_status_bar_hidden.set(val);
-        self.setNeedsStatusBarAppearanceUpdate();
-    }
-
-    pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) {
-        self.ivars().preferred_status_bar_style.set(val);
-        self.setNeedsStatusBarAppearanceUpdate();
-    }
-
-    pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
-        self.ivars().prefers_home_indicator_auto_hidden.set(val);
-        let os_capabilities = app_state::os_capabilities();
-        if os_capabilities.home_indicator_hidden {
-            self.setNeedsUpdateOfHomeIndicatorAutoHidden();
-        } else {
-            os_capabilities.home_indicator_hidden_err_msg("ignoring")
-        }
-    }
-
-    pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
-        self.ivars()
-            .preferred_screen_edges_deferring_system_gestures
-            .set(val);
-        let os_capabilities = app_state::os_capabilities();
-        if os_capabilities.defer_system_gestures {
-            self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
-        } else {
-            os_capabilities.defer_system_gestures_err_msg("ignoring")
-        }
-    }
-
-    pub(crate) fn set_supported_interface_orientations(
-        &self,
-        mtm: MainThreadMarker,
-        valid_orientations: ValidOrientations,
-    ) {
-        let mask = match (
-            valid_orientations,
-            UIDevice::current(mtm).userInterfaceIdiom(),
-        ) {
-            (ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => {
-                UIInterfaceOrientationMask::AllButUpsideDown
-            }
-            (ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
-            (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
-            (ValidOrientations::Portrait, UIUserInterfaceIdiom::Phone) => {
-                UIInterfaceOrientationMask::Portrait
-            }
-            (ValidOrientations::Portrait, _) => {
-                UIInterfaceOrientationMask::Portrait
-                    | UIInterfaceOrientationMask::PortraitUpsideDown
-            }
-        };
-        self.ivars().supported_orientations.set(mask);
-        UIViewController::attemptRotationToDeviceOrientation();
-    }
-
-    pub(crate) fn new(
-        mtm: MainThreadMarker,
-        window_attributes: &WindowAttributes,
-        view: &UIView,
-    ) -> Id<Self> {
-        // These are set properly below, we just to set them to something in the meantime.
-        let this = Self::alloc().set_ivars(ViewControllerState {
-            prefers_status_bar_hidden: Cell::new(false),
-            preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
-            prefers_home_indicator_auto_hidden: Cell::new(false),
-            supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
-            preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
-        });
-        let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
-
-        this.set_prefers_status_bar_hidden(
-            window_attributes
-                .platform_specific
-                .prefers_status_bar_hidden,
-        );
-
-        this.set_preferred_status_bar_style(
-            window_attributes
-                .platform_specific
-                .preferred_status_bar_style
-                .into(),
-        );
-
-        this.set_supported_interface_orientations(
-            mtm,
-            window_attributes.platform_specific.valid_orientations,
-        );
-
-        this.set_prefers_home_indicator_auto_hidden(
-            window_attributes
-                .platform_specific
-                .prefers_home_indicator_hidden,
-        );
-
-        this.set_preferred_screen_edges_deferring_system_gestures(
-            window_attributes
-                .platform_specific
-                .preferred_screen_edges_deferring_system_gestures
-                .into(),
-        );
-
-        this.setView(Some(view));
-
-        this
-    }
-}
-
-declare_class!(
-    #[derive(Debug, PartialEq, Eq, Hash)]
-    pub(crate) struct WinitUIWindow;
-
-    unsafe impl ClassType for WinitUIWindow {
-        #[inherits(UIResponder, NSObject)]
-        type Super = UIWindow;
-        type Mutability = mutability::InteriorMutable;
-        const NAME: &'static str = "WinitUIWindow";
-    }
-
-    impl DeclaredClass for WinitUIWindow {}
-
-    unsafe impl WinitUIWindow {
-        #[method(becomeKeyWindow)]
-        fn become_key_window(&self) {
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_event(
-                mtm,
-                EventWrapper::StaticEvent(Event::WindowEvent {
-                    window_id: RootWindowId(self.id()),
-                    event: WindowEvent::Focused(true),
-                }),
-            );
-            let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
-        }
-
-        #[method(resignKeyWindow)]
-        fn resign_key_window(&self) {
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_event(
-                mtm,
-                EventWrapper::StaticEvent(Event::WindowEvent {
-                    window_id: RootWindowId(self.id()),
-                    event: WindowEvent::Focused(false),
-                }),
-            );
-            let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
-        }
-    }
-);
-
-impl WinitUIWindow {
-    pub(crate) fn new(
-        mtm: MainThreadMarker,
-        window_attributes: &WindowAttributes,
-        frame: CGRect,
-        view_controller: &UIViewController,
-    ) -> Id<Self> {
-        let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
-
-        this.setRootViewController(Some(view_controller));
-
-        match window_attributes.fullscreen.clone().map(Into::into) {
-            Some(Fullscreen::Exclusive(ref video_mode)) => {
-                let monitor = video_mode.monitor();
-                let screen = monitor.ui_screen(mtm);
-                screen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
-                this.setScreen(screen);
-            }
-            Some(Fullscreen::Borderless(Some(ref monitor))) => {
-                let screen = monitor.ui_screen(mtm);
-                this.setScreen(screen);
-            }
-            _ => (),
-        }
-
-        this
-    }
-
-    pub(crate) fn id(&self) -> WindowId {
-        (self as *const Self as usize as u64).into()
-    }
-}
-
-declare_class!(
-    pub struct WinitApplicationDelegate;
-
-    unsafe impl ClassType for WinitApplicationDelegate {
-        type Super = NSObject;
-        type Mutability = mutability::InteriorMutable;
-        const NAME: &'static str = "WinitApplicationDelegate";
-    }
-
-    impl DeclaredClass for WinitApplicationDelegate {}
-
-    // UIApplicationDelegate protocol
-    unsafe impl WinitApplicationDelegate {
-        #[method(application:didFinishLaunchingWithOptions:)]
-        fn did_finish_launching(&self, _application: &UIApplication, _: *mut NSObject) -> bool {
-            app_state::did_finish_launching(MainThreadMarker::new().unwrap());
-            true
-        }
-
-        #[method(applicationDidBecomeActive:)]
-        fn did_become_active(&self, _application: &UIApplication) {
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Resumed))
-        }
-
-        #[method(applicationWillResignActive:)]
-        fn will_resign_active(&self, _application: &UIApplication) {
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::Suspended))
-        }
-
-        #[method(applicationWillEnterForeground:)]
-        fn will_enter_foreground(&self, application: &UIApplication) {
-            self.send_occluded_event_for_all_windows(application, false);
-        }
-
-        #[method(applicationDidEnterBackground:)]
-        fn did_enter_background(&self, application: &UIApplication) {
-            self.send_occluded_event_for_all_windows(application, true);
-        }
-
-        #[method(applicationWillTerminate:)]
-        fn will_terminate(&self, application: &UIApplication) {
-            let mut events = Vec::new();
-            for window in application.windows().iter() {
-                if window.is_kind_of::<WinitUIWindow>() {
-                    // SAFETY: We just checked that the window is a `winit` window
-                    let window = unsafe {
-                        let ptr: *const UIWindow = window;
-                        let ptr: *const WinitUIWindow = ptr.cast();
-                        &*ptr
-                    };
-                    events.push(EventWrapper::StaticEvent(Event::WindowEvent {
-                        window_id: RootWindowId(window.id()),
-                        event: WindowEvent::Destroyed,
-                    }));
-                }
-            }
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_events(mtm, events);
-            app_state::terminated(mtm);
-        }
-
-        #[method(applicationDidReceiveMemoryWarning:)]
-        fn did_receive_memory_warning(&self, _application: &UIApplication) {
-            let mtm = MainThreadMarker::new().unwrap();
-            app_state::handle_nonuser_event(mtm, EventWrapper::StaticEvent(Event::MemoryWarning))
-        }
-    }
-);
-
-impl WinitApplicationDelegate {
-    fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
-        let mut events = Vec::new();
-        for window in application.windows().iter() {
-            if window.is_kind_of::<WinitUIWindow>() {
-                // SAFETY: We just checked that the window is a `winit` window
-                let window = unsafe {
-                    let ptr: *const UIWindow = window;
-                    let ptr: *const WinitUIWindow = ptr.cast();
-                    &*ptr
-                };
-                events.push(EventWrapper::StaticEvent(Event::WindowEvent {
-                    window_id: RootWindowId(window.id()),
-                    event: WindowEvent::Occluded(occluded),
-                }));
-            }
-        }
-        let mtm = MainThreadMarker::new().unwrap();
-        app_state::handle_nonuser_events(mtm, events);
-    }
-}
diff --git a/src/platform_impl/ios/view_controller.rs b/src/platform_impl/ios/view_controller.rs
new file mode 100644
index 0000000000..5a324a498e
--- /dev/null
+++ b/src/platform_impl/ios/view_controller.rs
@@ -0,0 +1,183 @@
+use std::cell::Cell;
+
+use icrate::Foundation::{MainThreadMarker, NSObject};
+use objc2::rc::Id;
+use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
+
+use super::app_state::{self};
+use super::uikit::{
+    UIDevice, UIInterfaceOrientationMask, UIResponder, UIStatusBarStyle, UIView, UIViewController,
+};
+use crate::{
+    platform::ios::ValidOrientations,
+    platform_impl::platform::ffi::{UIRectEdge, UIUserInterfaceIdiom},
+    window::WindowAttributes,
+};
+
+pub struct ViewControllerState {
+    prefers_status_bar_hidden: Cell<bool>,
+    preferred_status_bar_style: Cell<UIStatusBarStyle>,
+    prefers_home_indicator_auto_hidden: Cell<bool>,
+    supported_orientations: Cell<UIInterfaceOrientationMask>,
+    preferred_screen_edges_deferring_system_gestures: Cell<UIRectEdge>,
+}
+
+declare_class!(
+    pub(crate) struct WinitViewController;
+
+    unsafe impl ClassType for WinitViewController {
+        #[inherits(UIResponder, NSObject)]
+        type Super = UIViewController;
+        type Mutability = mutability::InteriorMutable;
+        const NAME: &'static str = "WinitUIViewController";
+    }
+
+    impl DeclaredClass for WinitViewController {
+        type Ivars = ViewControllerState;
+    }
+
+    unsafe impl WinitViewController {
+        #[method(shouldAutorotate)]
+        fn should_autorotate(&self) -> bool {
+            true
+        }
+
+        #[method(prefersStatusBarHidden)]
+        fn prefers_status_bar_hidden(&self) -> bool {
+            self.ivars().prefers_status_bar_hidden.get()
+        }
+
+        #[method(preferredStatusBarStyle)]
+        fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
+            self.ivars().preferred_status_bar_style.get()
+        }
+
+        #[method(prefersHomeIndicatorAutoHidden)]
+        fn prefers_home_indicator_auto_hidden(&self) -> bool {
+            self.ivars().prefers_home_indicator_auto_hidden.get()
+        }
+
+        #[method(supportedInterfaceOrientations)]
+        fn supported_orientations(&self) -> UIInterfaceOrientationMask {
+            self.ivars().supported_orientations.get()
+        }
+
+        #[method(preferredScreenEdgesDeferringSystemGestures)]
+        fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
+            self.ivars()
+                .preferred_screen_edges_deferring_system_gestures
+                .get()
+        }
+    }
+);
+
+impl WinitViewController {
+    pub(crate) fn set_prefers_status_bar_hidden(&self, val: bool) {
+        self.ivars().prefers_status_bar_hidden.set(val);
+        self.setNeedsStatusBarAppearanceUpdate();
+    }
+
+    pub(crate) fn set_preferred_status_bar_style(&self, val: UIStatusBarStyle) {
+        self.ivars().preferred_status_bar_style.set(val);
+        self.setNeedsStatusBarAppearanceUpdate();
+    }
+
+    pub(crate) fn set_prefers_home_indicator_auto_hidden(&self, val: bool) {
+        self.ivars().prefers_home_indicator_auto_hidden.set(val);
+        let os_capabilities = app_state::os_capabilities();
+        if os_capabilities.home_indicator_hidden {
+            self.setNeedsUpdateOfHomeIndicatorAutoHidden();
+        } else {
+            os_capabilities.home_indicator_hidden_err_msg("ignoring")
+        }
+    }
+
+    pub(crate) fn set_preferred_screen_edges_deferring_system_gestures(&self, val: UIRectEdge) {
+        self.ivars()
+            .preferred_screen_edges_deferring_system_gestures
+            .set(val);
+        let os_capabilities = app_state::os_capabilities();
+        if os_capabilities.defer_system_gestures {
+            self.setNeedsUpdateOfScreenEdgesDeferringSystemGestures();
+        } else {
+            os_capabilities.defer_system_gestures_err_msg("ignoring")
+        }
+    }
+
+    pub(crate) fn set_supported_interface_orientations(
+        &self,
+        mtm: MainThreadMarker,
+        valid_orientations: ValidOrientations,
+    ) {
+        let mask = match (
+            valid_orientations,
+            UIDevice::current(mtm).userInterfaceIdiom(),
+        ) {
+            (ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => {
+                UIInterfaceOrientationMask::AllButUpsideDown
+            }
+            (ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
+            (ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
+            (ValidOrientations::Portrait, UIUserInterfaceIdiom::Phone) => {
+                UIInterfaceOrientationMask::Portrait
+            }
+            (ValidOrientations::Portrait, _) => {
+                UIInterfaceOrientationMask::Portrait
+                    | UIInterfaceOrientationMask::PortraitUpsideDown
+            }
+        };
+        self.ivars().supported_orientations.set(mask);
+        UIViewController::attemptRotationToDeviceOrientation();
+    }
+
+    pub(crate) fn new(
+        mtm: MainThreadMarker,
+        window_attributes: &WindowAttributes,
+        view: &UIView,
+    ) -> Id<Self> {
+        // These are set properly below, we just to set them to something in the meantime.
+        let this = Self::alloc().set_ivars(ViewControllerState {
+            prefers_status_bar_hidden: Cell::new(false),
+            preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
+            prefers_home_indicator_auto_hidden: Cell::new(false),
+            supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
+            preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
+        });
+        let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
+
+        this.set_prefers_status_bar_hidden(
+            window_attributes
+                .platform_specific
+                .prefers_status_bar_hidden,
+        );
+
+        this.set_preferred_status_bar_style(
+            window_attributes
+                .platform_specific
+                .preferred_status_bar_style
+                .into(),
+        );
+
+        this.set_supported_interface_orientations(
+            mtm,
+            window_attributes.platform_specific.valid_orientations,
+        );
+
+        this.set_prefers_home_indicator_auto_hidden(
+            window_attributes
+                .platform_specific
+                .prefers_home_indicator_hidden,
+        );
+
+        this.set_preferred_screen_edges_deferring_system_gestures(
+            window_attributes
+                .platform_specific
+                .preferred_screen_edges_deferring_system_gestures
+                .into(),
+        );
+
+        this.setView(Some(view));
+
+        this
+    }
+}
diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs
index d37b10cdfc..81e88e5635 100644
--- a/src/platform_impl/ios/window.rs
+++ b/src/platform_impl/ios/window.rs
@@ -5,12 +5,15 @@ use std::collections::VecDeque;
 use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
 use log::{debug, warn};
 use objc2::rc::Id;
-use objc2::runtime::AnyObject;
-use objc2::{class, msg_send};
+use objc2::runtime::{AnyObject, NSObject};
+use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
 
 use super::app_state::EventWrapper;
-use super::uikit::{UIApplication, UIScreen, UIScreenOverscanCompensation};
-use super::view::{WinitUIWindow, WinitView, WinitViewController};
+use super::uikit::{
+    UIApplication, UIResponder, UIScreen, UIScreenOverscanCompensation, UIViewController, UIWindow,
+};
+use super::view::WinitView;
+use super::view_controller::WinitViewController;
 use crate::{
     cursor::Cursor,
     dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
@@ -25,6 +28,81 @@ use crate::{
     },
 };
 
+declare_class!(
+    #[derive(Debug, PartialEq, Eq, Hash)]
+    pub(crate) struct WinitUIWindow;
+
+    unsafe impl ClassType for WinitUIWindow {
+        #[inherits(UIResponder, NSObject)]
+        type Super = UIWindow;
+        type Mutability = mutability::InteriorMutable;
+        const NAME: &'static str = "WinitUIWindow";
+    }
+
+    impl DeclaredClass for WinitUIWindow {}
+
+    unsafe impl WinitUIWindow {
+        #[method(becomeKeyWindow)]
+        fn become_key_window(&self) {
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_event(
+                mtm,
+                EventWrapper::StaticEvent(Event::WindowEvent {
+                    window_id: RootWindowId(self.id()),
+                    event: WindowEvent::Focused(true),
+                }),
+            );
+            let _: () = unsafe { msg_send![super(self), becomeKeyWindow] };
+        }
+
+        #[method(resignKeyWindow)]
+        fn resign_key_window(&self) {
+            let mtm = MainThreadMarker::new().unwrap();
+            app_state::handle_nonuser_event(
+                mtm,
+                EventWrapper::StaticEvent(Event::WindowEvent {
+                    window_id: RootWindowId(self.id()),
+                    event: WindowEvent::Focused(false),
+                }),
+            );
+            let _: () = unsafe { msg_send![super(self), resignKeyWindow] };
+        }
+    }
+);
+
+impl WinitUIWindow {
+    pub(crate) fn new(
+        mtm: MainThreadMarker,
+        window_attributes: &WindowAttributes,
+        frame: CGRect,
+        view_controller: &UIViewController,
+    ) -> Id<Self> {
+        let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
+
+        this.setRootViewController(Some(view_controller));
+
+        match window_attributes.fullscreen.clone().map(Into::into) {
+            Some(Fullscreen::Exclusive(ref video_mode)) => {
+                let monitor = video_mode.monitor();
+                let screen = monitor.ui_screen(mtm);
+                screen.setCurrentMode(Some(video_mode.screen_mode(mtm)));
+                this.setScreen(screen);
+            }
+            Some(Fullscreen::Borderless(Some(ref monitor))) => {
+                let screen = monitor.ui_screen(mtm);
+                this.setScreen(screen);
+            }
+            _ => (),
+        }
+
+        this
+    }
+
+    pub(crate) fn id(&self) -> WindowId {
+        (self as *const Self as usize as u64).into()
+    }
+}
+
 pub struct Inner {
     window: Id<WinitUIWindow>,
     view_controller: Id<WinitViewController>,