egui_winit/
safe_area.rs

1#[cfg(target_os = "ios")]
2pub use ios::get_safe_area_insets;
3
4#[cfg(target_os = "ios")]
5mod ios {
6    use egui::{SafeAreaInsets, epaint::MarginF32};
7    use objc2::{ClassType, rc::Retained};
8    use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
9    use objc2_ui_kit::{UIApplication, UISceneActivationState, UIWindowScene};
10
11    /// Gets the ios safe area insets.
12    ///
13    /// A safe area defines the area within a view that isn’t covered by a navigation bar, tab bar,
14    /// toolbar, or other views a window might provide. Safe areas are essential for avoiding a
15    /// device’s interactive and display features, like Dynamic Island on iPhone or the camera
16    /// housing on some Mac models.
17    ///
18    /// Once winit v0.31 has been released this can be removed in favor of
19    /// `winit::Window::safe_area`.
20    pub fn get_safe_area_insets() -> SafeAreaInsets {
21        let Some(main_thread_marker) = MainThreadMarker::new() else {
22            log::error!("Getting safe area insets needs to be performed on the main thread");
23            return SafeAreaInsets::default();
24        };
25
26        let app = UIApplication::sharedApplication(main_thread_marker);
27
28        #[allow(unsafe_code)]
29        unsafe {
30            // Look for the first window scene that's in the foreground
31            for scene in app.connectedScenes() {
32                if scene.isKindOfClass(UIWindowScene::class())
33                    && matches!(
34                        scene.activationState(),
35                        UISceneActivationState::ForegroundActive
36                            | UISceneActivationState::ForegroundInactive
37                    )
38                {
39                    // Safe to cast, the class kind was checked above
40                    let window_scene = Retained::cast::<UIWindowScene>(scene.clone());
41                    if let Some(window) = window_scene.keyWindow() {
42                        let insets = window.safeAreaInsets();
43                        return SafeAreaInsets(MarginF32 {
44                            top: insets.top as f32,
45                            left: insets.left as f32,
46                            right: insets.right as f32,
47                            bottom: insets.bottom as f32,
48                        });
49                    }
50                }
51            }
52        }
53
54        SafeAreaInsets::default()
55    }
56}