本篇主要通過四個方面來解析屏幕旋轉(zhuǎn):
1彪腔、實現(xiàn)旋轉(zhuǎn)的方式之跟隨手機感應(yīng)旋轉(zhuǎn)
2稚疹、實現(xiàn)旋轉(zhuǎn)的方式之手動旋轉(zhuǎn)
3、屏幕旋轉(zhuǎn)的場景應(yīng)用
4啦扬、易混淆的枚舉值
下面來逐條分析:
一、跟隨手機感應(yīng)器旋轉(zhuǎn)
1. 兩種方法可以設(shè)置屏幕旋轉(zhuǎn)的全局權(quán)限:
1.1 Device Orientation 屬性配置:
新建項目 -> TARGET -> General -> Deployment Info
Device Orientation
- iPhone 默認豎屏展示, 當(dāng)系統(tǒng)屏幕旋轉(zhuǎn)開關(guān)未鎖定時, 就可以自由的轉(zhuǎn)動, 值得注意的是
iPhone 不支持旋轉(zhuǎn)到 Upside Down 方向
凫碌。 - iPad 默認豎屏展示, 當(dāng)系統(tǒng)屏幕旋轉(zhuǎn)開關(guān)未鎖定時, 伴隨iPad方向展示, 就可以自由的轉(zhuǎn)動扑毡。
系統(tǒng)屏幕旋轉(zhuǎn)開關(guān)
1.2 實現(xiàn)Appdelegate 的supportedInterfaceOrientationsForWindow 方法:
// 返回需要支持的方向
// 如果我們實現(xiàn)了Appdelegate的這一方法,那么我們的App的全局旋轉(zhuǎn)設(shè)置將以這里的為準
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
總結(jié): 以上兩種方式優(yōu)先級:
Appdelegate方法 > Target 配置
盛险。
2. 單一控制器處理屏幕跟隨手機感應(yīng)器旋轉(zhuǎn)
只要讓當(dāng)前的視圖控制器實現(xiàn)以下三個方法:
/** 是否自動旋轉(zhuǎn)
- iOS16.0已過期, 不會再調(diào)用.
- iOS16.0 之前, 先調(diào)用supportedInterfaceOrientations 確定支持的屏幕方向, 再調(diào)用shouldAutorotate 是否自動旋轉(zhuǎn), 來確定控制器方向.
- iOS16.0 之前, preferredInterfaceOrientationForPresentation 并未調(diào)用.
- iOS16.0 之前, return NO 之后, 當(dāng)前控制器都支持屏幕旋轉(zhuǎn)了
*/
- (BOOL)shouldAutorotate {
return YES;
}
/// 當(dāng)前控制器支持的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
/// 優(yōu)先的屏幕方向 - 只會在 presentViewController:animated:completion時被調(diào)用
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
這種方法需要注意以下幾點:
-
shouldAutorotate
返回 YES 表示跟隨系統(tǒng)旋轉(zhuǎn)瞄摊,但是受supportedInterfaceOrientations
方法的返回值影響,只支持跟隨手機傳感器旋轉(zhuǎn)到支持的方向. -
preferredInterfaceOrientationForPresentation
需要返回supportedInterfaceOrientations
中支持的方向苦掘,不然會發(fā)生'UIApplicationInvalidInterfaceOrientation'
崩潰. -
iOS16.0 之后
單純只會調(diào)用supportedInterfaceOrientations
,shouldAutorotate
就不會被調(diào)用了换帜。
3. 既配置了屏幕旋轉(zhuǎn)的全局權(quán)限, 也配置了單一控制器屏幕旋轉(zhuǎn)權(quán)限
1.1
和1.2
兩種方式的配置和控制器的 supportedInterfaceOrientations
方法(2)都會影響最終視圖控制器最終支持的方向。
以 iOS 16 之前
以 present
打開控制器的方式為例鹤啡,當(dāng)前控制器最終支持的屏幕方向惯驼,取決于上面兩種方式中的優(yōu)先級最高的方式的值,與控制器 supportedInterfaceOrientations
的交集递瑰。
總結(jié)起來有以下幾種情況:
- 如果交集為空祟牲,且在控制器的
shouldAutorotate
方法中返回為YES
,則會發(fā)生UIApplicationInvalidInterfaceOrientation
的崩潰泣矛。 - 如果交集為空疲眷,且在控制器的
shouldAutorotate
方法中返回為NO
,控制器的supportedInterfaceOrientations
方法與preferredInterfaceOrientationForPresentation
方法返回值不沖突(前者返回值包含有后者返回值)您朽,則顯示為控制器配置的方向狂丝。 - 如果交集為空,且在控制器的
shouldAutorotate
方法中返回為NO
哗总,控制器的supportedInterfaceOrientations
方法與preferredInterfaceOrientationForPresentation
方法返回值沖突(前者返回值未包含有后者返回值)几颜,則會發(fā)生UIApplicationInvalidInterfaceOrientation
的崩潰。 - 如果交集不為空讯屈,控制器的
supportedInterfaceOrientations
方法與preferredInterfaceOrientationForPresentation
方法返回值沖突蛋哭,則會發(fā)生UIApplicationInvalidInterfaceOrientation
的崩潰。 - 如果交集不為空涮母,控制器的
supportedInterfaceOrientations
方法與preferredInterfaceOrientationForPresentation
方法返回值不沖突谆趾,當(dāng)前控制器則根據(jù)shouldAutorotate
返回值決定是否在交集的方向內(nèi)自動旋轉(zhuǎn)躁愿。
所以, 這里建議如果沒有全局配置的需求,就不要變更 Target 屬性配置或?qū)崿F(xiàn) Appdelegate 方法沪蓬,只需在要實現(xiàn)旋轉(zhuǎn)效果的 ViewController 中按前面所說的方式去實現(xiàn)代碼彤钟。
而在 iOS 16
之后, 所有崩潰的問題都被優(yōu)化了, 同樣, 當(dāng)前控制器最終支持的屏幕方向,取決于上面兩種方式中的優(yōu)先級最高的方式的值跷叉,與控制器 supportedInterfaceOrientations
的交集逸雹。
總結(jié)起來有以下幾種情況:
- 如果交集為空,以控制器
supportedInterfaceOrientations
方法返回值為準云挟。 - 如果交集不為空梆砸,且控制器
supportedInterfaceOrientations
方法與preferredInterfaceOrientationForPresentation
方法返回值不沖突,則以交集為準园欣。
二帖世、手動旋轉(zhuǎn)屏幕
這種方式在很多視頻軟件中都很常見,點擊按鈕后旋轉(zhuǎn)至橫屏俊庇。
在iOS 16 之前需要在 shouldAutorotate
中返回 yes
狮暑,然后再在此方法中 UIInterfaceOrientation
傳入你需要旋轉(zhuǎn)到的方向。
- (void)changeVCToOrientation:(UIInterfaceOrientation)orientation {
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
SEL selector = NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
}
在iOS 16之后你會發(fā)現(xiàn), 它已經(jīng)失效了, fix如下:
升級了Xcode14+, 適配iOS16 API
未升級Xcode14+, 在舊版適配新版本 API
三辉饱、屏幕旋轉(zhuǎn)的場景應(yīng)用
- 自動旋轉(zhuǎn)
如果你的 iPhone 沒有關(guān)閉系統(tǒng)屏幕旋轉(zhuǎn),你就能發(fā)現(xiàn)系統(tǒng)相冊 APP 的頁面是可以跟著手機轉(zhuǎn)動方向旋轉(zhuǎn)的拣展。
如果你想實現(xiàn)和它一樣的效果彭沼,只需要按照前面方式一(跟隨手機感應(yīng)器旋轉(zhuǎn))去配置你的視圖控制器的方法,之后控制器就可以在 supportedInterfaceOrientations
返回的方向內(nèi)實現(xiàn)自由旋轉(zhuǎn)了备埃。
- 只能手動旋轉(zhuǎn)
這種場景比較少見姓惑,在視頻直播類 APP 中常見的場景是自動和手動旋轉(zhuǎn)相結(jié)合的方式。
如果你要實現(xiàn)只能通過像點擊按鈕去旋轉(zhuǎn)的方式按脚,首先需要在supportedInterfaceOrientations
方法中返回你需要支持的方向于毙,這里重點是shouldAutorotate
方法的返回值。
上面方式二中(手動旋轉(zhuǎn))說明了手動旋轉(zhuǎn)需要 shouldAutorotate
返回 YES
辅搬,但是這也會讓控制器支持自動旋轉(zhuǎn)唯沮,不符合這個需求,所以我們按以下方法處理:
- (BOOL)shouldAutorotate {
if (self.isRotationNeeded) {
return YES;
} else {
return NO;
}
}
屬性 isRotationNeeded
作為是否需要旋轉(zhuǎn)的標記堪遂,isRotationNeeded
默認為 NO
介蛉,此時就算你旋轉(zhuǎn)設(shè)備,回調(diào) shouldAutorotate
方法時也不會返回 YES
溶褪,所以屏幕也不會自動旋轉(zhuǎn)币旧。
剩下的只需要你在點擊旋轉(zhuǎn)的按鈕后將isRotationNeeded
置為YES
并調(diào)用手動旋轉(zhuǎn)的方法,這樣處理后只能手動旋轉(zhuǎn)的效果就實現(xiàn)了猿妈。
4吹菱、相關(guān)枚舉值
4.1 設(shè)備方向:UIDeviceOrientation
UIDeviceOrientation 是以home 鍵的位置作為參照巍虫,枚舉值指的是設(shè)備倒向的方向, 受傳感器影響,和當(dāng)前屏幕顯示的方向無關(guān)鳍刷,所以只能取值不能設(shè)值
垫言。
前面講述的屏幕旋轉(zhuǎn)方法中不會直接用到這個枚舉,但是如果你有監(jiān)聽設(shè)備當(dāng)前方向的需求時倾剿,它就變得有用了筷频。可以通過 [UIDevice currentDevice].orientation 獲取當(dāng)前設(shè)備的方向前痘,若要監(jiān)聽設(shè)備的方向變化凛捏,可以用以下代碼實現(xiàn):
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:observer
selector:@selector(onDeviceOrientationChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
- (void)onDeviceOrientationChange:(NSNotification *)noti {
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
switch (deviceOrientation) {
case UIDeviceOrientationPortrait:
NSLog(@"豎屏");
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"橫屏(Home在右邊祥款,設(shè)備向左邊倒)");
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"橫屏(Home在左邊弥咪,設(shè)備向右邊倒)");
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"Home在上邊");
break;
case UIDeviceOrientationFaceUp:
NSLog(@"屏幕向上");
break;
case UIDeviceOrientationFaceDown:
NSLog(@"屏幕向下");
break;
default:
break;
}
}
4.2 頁面方向:UIInterfaceOrientation
UIInterfaceOrientation 是當(dāng)前視圖控制器的方向被济,區(qū)別于設(shè)備方向娱挨,它是屏幕正在顯示的方向, 也就是當(dāng)前頁面home所處的方向幕袱。
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} API_UNAVAILABLE(tvos);
4.3 頁面方向:UIInterfaceOrientationMask
觀察 UIInterfaceOrientationMask 枚舉的值吼畏,我們就會發(fā)現(xiàn)這是一種為了支持多種 UIInterfaceOrientation 而定義的類型妖啥,它用來作為 supportedInterfaceOrientations 方法的返回值陨晶,比如我們在該方法中返回 UIInterfaceOrientationMaskAll 就可以支持所有方向了芝硬。
/// 當(dāng)前 VC支持的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} API_UNAVAILABLE(tvos);
結(jié)語
路漫漫其修遠兮蚜点,吾將上下而求索~
.End