iOS 屏幕旋轉(zhuǎn)的實踐解析

本篇主要通過四個方面來解析屏幕旋轉(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.11.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é)語

路漫漫其修遠兮蚜点,吾將上下而求索~

作者簡書

作者掘金

作者GitHub

.End

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拌阴,隨后出現(xiàn)的幾起案子绍绘,更是在濱河造成了極大的恐慌,老刑警劉巖迟赃,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陪拘,死亡現(xiàn)場離奇詭異,居然都是意外死亡纤壁,警方通過查閱死者的電腦和手機左刽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酌媒,“玉大人欠痴,你說我怎么就攤上這事♀捎樱” “怎么了斋否?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拭荤。 經(jīng)常有香客問我茵臭,道長,這世上最難降的妖魔是什么舅世? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任旦委,我火速辦了婚禮奇徒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘缨硝。我一直安慰自己摩钙,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布查辩。 她就那樣靜靜地躺著胖笛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宜岛。 梳的紋絲不亂的頭發(fā)上长踊,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機與錄音萍倡,去河邊找鬼身弊。 笑死,一個胖子當(dāng)著我的面吹牛列敲,可吹牛的內(nèi)容都是我干的阱佛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼戴而,長吁一口氣:“原來是場噩夢啊……” “哼凑术!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起填硕,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤麦萤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扁眯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡翅帜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年姻檀,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝滴。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡绣版,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歼疮,到底是詐尸還是另有隱情杂抽,我是刑警寧澤,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布韩脏,位于F島的核電站缩麸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赡矢。R本人自食惡果不足惜杭朱,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一阅仔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧弧械,春花似錦八酒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至画饥,卻和暖如春衔瓮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荒澡。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工报辱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人单山。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓碍现,卻偏偏與公主長得像,于是被迫代替她去往敵國和親米奸。 傳聞我的和親對象是個殘疾皇子昼接,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容