winit/platform/ios.rs
1//! # iOS / UIKit
2//!
3//! Winit has an OS requirement of iOS 8 or higher, and is regularly tested on
4//! iOS 9.3.
5//!
6//! iOS's main `UIApplicationMain` does some init work that's required by all
7//! UI-related code (see issue [#1705]). It is best to create your windows
8//! inside `Event::Resumed`.
9//!
10//! [#1705]: https://github.com/rust-windowing/winit/issues/1705
11//!
12//! ## Building app
13//!
14//! To build ios app you will need rustc built for this targets:
15//!
16//! - armv7-apple-ios
17//! - armv7s-apple-ios
18//! - i386-apple-ios
19//! - aarch64-apple-ios
20//! - x86_64-apple-ios
21//!
22//! Then
23//!
24//! ```
25//! cargo build --target=...
26//! ```
27//! The simplest way to integrate your app into xcode environment is to build it
28//! as a static library. Wrap your main function and export it.
29//!
30//! ```rust, ignore
31//! #[no_mangle]
32//! pub extern fn start_winit_app() {
33//! start_inner()
34//! }
35//!
36//! fn start_inner() {
37//! ...
38//! }
39//! ```
40//!
41//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
42//!
43//! ```ignore
44//! void start_winit_app();
45//! ```
46//!
47//! Use start_winit_app inside your xcode's main function.
48//!
49//!
50//! ## App lifecycle and events
51//!
52//! iOS environment is very different from other platforms and you must be very
53//! careful with it's events. Familiarize yourself with
54//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
55//!
56//! This is how those event are represented in winit:
57//!
58//! - applicationDidBecomeActive is Resumed
59//! - applicationWillResignActive is Suspended
60//! - applicationWillTerminate is LoopExiting
61//!
62//! Keep in mind that after LoopExiting event is received every attempt to draw with
63//! opengl will result in segfault.
64//!
65//! Also note that app may not receive the LoopExiting event if suspended; it might be SIGKILL'ed.
66
67use std::os::raw::c_void;
68
69use crate::event_loop::EventLoop;
70use crate::monitor::{MonitorHandle, VideoModeHandle};
71use crate::window::{Window, WindowAttributes};
72
73/// Additional methods on [`EventLoop`] that are specific to iOS.
74pub trait EventLoopExtIOS {
75 /// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
76 fn idiom(&self) -> Idiom;
77}
78
79impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
80 fn idiom(&self) -> Idiom {
81 self.event_loop.idiom()
82 }
83}
84
85/// Additional methods on [`Window`] that are specific to iOS.
86pub trait WindowExtIOS {
87 /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
88 ///
89 /// The default value is device dependent, and it's recommended GLES or Metal applications set
90 /// this to [`MonitorHandle::scale_factor()`].
91 ///
92 /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
93 /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
94 fn set_scale_factor(&self, scale_factor: f64);
95
96 /// Sets the valid orientations for the [`Window`].
97 ///
98 /// The default value is [`ValidOrientations::LandscapeAndPortrait`].
99 ///
100 /// This changes the value returned by
101 /// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc),
102 /// and then calls
103 /// [`-[UIViewController attemptRotationToDeviceOrientation]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621400-attemptrotationtodeviceorientati?language=objc).
104 fn set_valid_orientations(&self, valid_orientations: ValidOrientations);
105
106 /// Sets whether the [`Window`] prefers the home indicator hidden.
107 ///
108 /// The default is to prefer showing the home indicator.
109 ///
110 /// This changes the value returned by
111 /// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc),
112 /// and then calls
113 /// [`-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc).
114 ///
115 /// This only has an effect on iOS 11.0+.
116 fn set_prefers_home_indicator_hidden(&self, hidden: bool);
117
118 /// Sets the screen edges for which the system gestures will take a lower priority than the
119 /// application's touch handling.
120 ///
121 /// This changes the value returned by
122 /// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc),
123 /// and then calls
124 /// [`-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc).
125 ///
126 /// This only has an effect on iOS 11.0+.
127 fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge);
128
129 /// Sets whether the [`Window`] prefers the status bar hidden.
130 ///
131 /// The default is to prefer showing the status bar.
132 ///
133 /// This sets the value of the
134 /// [`prefersStatusBarHidden`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc)
135 /// property.
136 ///
137 /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
138 /// is also called for you.
139 fn set_prefers_status_bar_hidden(&self, hidden: bool);
140
141 /// Sets the preferred status bar style for the [`Window`].
142 ///
143 /// The default is system-defined.
144 ///
145 /// This sets the value of the
146 /// [`preferredStatusBarStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc)
147 /// property.
148 ///
149 /// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
150 /// is also called for you.
151 fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle);
152
153 /// Sets whether the [`Window`] should recognize pinch gestures.
154 ///
155 /// The default is to not recognize gestures.
156 fn recognize_pinch_gesture(&self, should_recognize: bool);
157
158 /// Sets whether the [`Window`] should recognize pan gestures.
159 ///
160 /// The default is to not recognize gestures.
161 /// Installs [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer) onto view
162 ///
163 /// Set the minimum number of touches required: [`minimumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-minimumnumberoftouches)
164 ///
165 /// Set the maximum number of touches recognized: [`maximumNumberOfTouches`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer/1621208-maximumnumberoftouches)
166 fn recognize_pan_gesture(
167 &self,
168 should_recognize: bool,
169 minimum_number_of_touches: u8,
170 maximum_number_of_touches: u8,
171 );
172
173 /// Sets whether the [`Window`] should recognize double tap gestures.
174 ///
175 /// The default is to not recognize gestures.
176 fn recognize_doubletap_gesture(&self, should_recognize: bool);
177
178 /// Sets whether the [`Window`] should recognize rotation gestures.
179 ///
180 /// The default is to not recognize gestures.
181 fn recognize_rotation_gesture(&self, should_recognize: bool);
182}
183
184impl WindowExtIOS for Window {
185 #[inline]
186 fn set_scale_factor(&self, scale_factor: f64) {
187 self.window.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
188 }
189
190 #[inline]
191 fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
192 self.window.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
193 }
194
195 #[inline]
196 fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
197 self.window.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
198 }
199
200 #[inline]
201 fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
202 self.window.maybe_queue_on_main(move |w| {
203 w.set_preferred_screen_edges_deferring_system_gestures(edges)
204 })
205 }
206
207 #[inline]
208 fn set_prefers_status_bar_hidden(&self, hidden: bool) {
209 self.window.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
210 }
211
212 #[inline]
213 fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
214 self.window.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
215 }
216
217 #[inline]
218 fn recognize_pinch_gesture(&self, should_recognize: bool) {
219 self.window.maybe_queue_on_main(move |w| w.recognize_pinch_gesture(should_recognize));
220 }
221
222 #[inline]
223 fn recognize_pan_gesture(
224 &self,
225 should_recognize: bool,
226 minimum_number_of_touches: u8,
227 maximum_number_of_touches: u8,
228 ) {
229 self.window.maybe_queue_on_main(move |w| {
230 w.recognize_pan_gesture(
231 should_recognize,
232 minimum_number_of_touches,
233 maximum_number_of_touches,
234 )
235 });
236 }
237
238 #[inline]
239 fn recognize_doubletap_gesture(&self, should_recognize: bool) {
240 self.window.maybe_queue_on_main(move |w| w.recognize_doubletap_gesture(should_recognize));
241 }
242
243 #[inline]
244 fn recognize_rotation_gesture(&self, should_recognize: bool) {
245 self.window.maybe_queue_on_main(move |w| w.recognize_rotation_gesture(should_recognize));
246 }
247}
248
249/// Additional methods on [`WindowAttributes`] that are specific to iOS.
250pub trait WindowAttributesExtIOS {
251 /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
252 ///
253 /// The default value is device dependent, and it's recommended GLES or Metal applications set
254 /// this to [`MonitorHandle::scale_factor()`].
255 ///
256 /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
257 /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
258 fn with_scale_factor(self, scale_factor: f64) -> Self;
259
260 /// Sets the valid orientations for the [`Window`].
261 ///
262 /// The default value is [`ValidOrientations::LandscapeAndPortrait`].
263 ///
264 /// This sets the initial value returned by
265 /// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc).
266 fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> Self;
267
268 /// Sets whether the [`Window`] prefers the home indicator hidden.
269 ///
270 /// The default is to prefer showing the home indicator.
271 ///
272 /// This sets the initial value returned by
273 /// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
274 ///
275 /// This only has an effect on iOS 11.0+.
276 fn with_prefers_home_indicator_hidden(self, hidden: bool) -> Self;
277
278 /// Sets the screen edges for which the system gestures will take a lower priority than the
279 /// application's touch handling.
280 ///
281 /// This sets the initial value returned by
282 /// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
283 ///
284 /// This only has an effect on iOS 11.0+.
285 fn with_preferred_screen_edges_deferring_system_gestures(self, edges: ScreenEdge) -> Self;
286
287 /// Sets whether the [`Window`] prefers the status bar hidden.
288 ///
289 /// The default is to prefer showing the status bar.
290 ///
291 /// This sets the initial value returned by
292 /// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
293 fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self;
294
295 /// Sets the style of the [`Window`]'s status bar.
296 ///
297 /// The default is system-defined.
298 ///
299 /// This sets the initial value returned by
300 /// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc),
301 fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self;
302}
303
304impl WindowAttributesExtIOS for WindowAttributes {
305 #[inline]
306 fn with_scale_factor(mut self, scale_factor: f64) -> Self {
307 self.platform_specific.scale_factor = Some(scale_factor);
308 self
309 }
310
311 #[inline]
312 fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
313 self.platform_specific.valid_orientations = valid_orientations;
314 self
315 }
316
317 #[inline]
318 fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
319 self.platform_specific.prefers_home_indicator_hidden = hidden;
320 self
321 }
322
323 #[inline]
324 fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
325 self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges;
326 self
327 }
328
329 #[inline]
330 fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
331 self.platform_specific.prefers_status_bar_hidden = hidden;
332 self
333 }
334
335 #[inline]
336 fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
337 self.platform_specific.preferred_status_bar_style = status_bar_style;
338 self
339 }
340}
341
342/// Additional methods on [`MonitorHandle`] that are specific to iOS.
343pub trait MonitorHandleExtIOS {
344 /// Returns a pointer to the [`UIScreen`] that is used by this monitor.
345 ///
346 /// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
347 fn ui_screen(&self) -> *mut c_void;
348
349 /// Returns the preferred [`VideoModeHandle`] for this monitor.
350 ///
351 /// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
352 fn preferred_video_mode(&self) -> VideoModeHandle;
353}
354
355impl MonitorHandleExtIOS for MonitorHandle {
356 #[inline]
357 fn ui_screen(&self) -> *mut c_void {
358 // SAFETY: The marker is only used to get the pointer of the screen
359 let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
360 objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
361 }
362
363 #[inline]
364 fn preferred_video_mode(&self) -> VideoModeHandle {
365 VideoModeHandle { video_mode: self.inner.preferred_video_mode() }
366 }
367}
368
369/// Valid orientations for a particular [`Window`].
370#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
371pub enum ValidOrientations {
372 /// Excludes `PortraitUpsideDown` on iphone
373 #[default]
374 LandscapeAndPortrait,
375
376 Landscape,
377
378 /// Excludes `PortraitUpsideDown` on iphone
379 Portrait,
380}
381
382/// The device [idiom].
383///
384/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
385#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
386pub enum Idiom {
387 Unspecified,
388
389 /// iPhone and iPod touch.
390 Phone,
391
392 /// iPad.
393 Pad,
394
395 /// tvOS and Apple TV.
396 TV,
397 CarPlay,
398}
399
400bitflags::bitflags! {
401 /// The [edges] of a screen.
402 ///
403 /// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
404 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
405 pub struct ScreenEdge: u8 {
406 const NONE = 0;
407 const TOP = 1 << 0;
408 const LEFT = 1 << 1;
409 const BOTTOM = 1 << 2;
410 const RIGHT = 1 << 3;
411 const ALL = ScreenEdge::TOP.bits() | ScreenEdge::LEFT.bits()
412 | ScreenEdge::BOTTOM.bits() | ScreenEdge::RIGHT.bits();
413 }
414}
415
416#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
417pub enum StatusBarStyle {
418 #[default]
419 Default,
420 LightContent,
421 DarkContent,
422}