iOS16 橫豎屏切換適配

項(xiàng)目中針對(duì)某一個(gè) View 需要進(jìn)行橫屏,在 iOS16 之前的方式大部分都是采取設(shè)置設(shè)備的方向來實(shí)現(xiàn)的,但是在 iOS16 開始這種方式已經(jīng)無效了,如果使用設(shè)置設(shè)備方向來實(shí)現(xiàn)橫豎屏切換,在 Xcode 的控制臺(tái)中會(huì)輸出以下信息:

[Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use UIWindowScene.requestGeometryUpdate(_:)

所以在 iOS16 開始如果要實(shí)現(xiàn)橫豎屏切換,需要使用 UIWindowScene 的方式進(jìn)行

iOS16 之前實(shí)現(xiàn)橫豎屏切換方式

AppDelegate

AppDelegate 的 .h 文件中添加一個(gè)變量來記錄是否需要進(jìn)行橫豎屏切換

@property (nonatomic, assign, getter=isLaunchScreen) BOOL launchScreen;    /**< 是否是橫屏 */

AppDelegate 的 .m 文件中重寫 launchScreensetter 方法

- (void)setLaunchScreen:(BOOL)launchScreen {

    _launchScreen = launchScreen;
    [self application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:nil];
}

并且實(shí)現(xiàn) UIApplicationDelegateapplication:supportedInterfaceOrientationsForWindow: 方法

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {

    if (self.isLaunchScreen) {
        // 只支持橫屏,并且 Home 按鍵在右邊
        return UIInterfaceOrientationMaskLandscapeRight;
    }

    // 只支持豎屏
    return UIInterfaceOrientationMaskPortrait;
}

在需要實(shí)現(xiàn)橫豎屏切換的 View

接下來在需要切換橫豎屏的 View 中增加以下方法,就能在 iOS16 之前實(shí)現(xiàn)該功能

/// 切換設(shè)備方向
/// - Parameter isLaunchScreen: 是否是全屏
- (void)p_switchOrientationWithLaunchScreen:(BOOL)isLaunchScreen {

    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    if (isLaunchScreen) {
        // 全屏操作
        appdelegate.launchScreen = YES;
    } else {
        // 退出全屏操作
        appdelegate.launchScreen = NO;
    }
    // 設(shè)置設(shè)備的方向
    [self p_swichToNewOrientation:isLaunchScreen ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait];
}
/// iOS16 之前進(jìn)行橫豎屏切換方式
/// - Parameter interfaceOrientation: 需要切換的方向
- (void)p_swichToNewOrientation:(UIInterfaceOrientation)interfaceOrientation {

    NSNumber *orientationTarget = [NSNumber numberWithInteger:interfaceOrientation];
    [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];
}

經(jīng)過以上代碼,就能實(shí)現(xiàn)在 iOS16 之前的設(shè)備上進(jìn)行橫豎屏切換,下面開始適配 iOS16 的橫豎屏切換

iOS16 實(shí)現(xiàn)橫豎屏切換

AppDelegate

iOS16 之前方式一樣,需要設(shè)置 launchScreen 標(biāo)志變量,重寫 launchScreensetter 方法,實(shí)現(xiàn) UIApplicationDelegateapplication:supportedInterfaceOrientationsForWindow: 方法

在需要實(shí)現(xiàn)橫豎屏切換的 View

p_switchOrientationWithLaunchScreen: 方法中增加 iOS16 適配

/// 切換設(shè)備方向
/// - Parameter isLaunchScreen: 是否是全屏
- (void)p_switchOrientationWithLaunchScreen:(BOOL)isLaunchScreen {

    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    if (isLaunchScreen) {
        // 全屏操作
        appdelegate.launchScreen = YES;
    } else {
        // 退出全屏操作
        appdelegate.launchScreen = NO;
    }

    if (@available(iOS 16.0, *)) {
        // setNeedsUpdateOfSupportedInterfaceOrientations 方法是 UIViewController 的方法,所以這個(gè)操作最好是放在控制器中去操作
        [self setNeedsUpdateOfSupportedInterfaceOrientations];
        NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
        UIWindowScene *scene = [array firstObject];
        // 屏幕方向
        UIInterfaceOrientationMask orientation = isLaunchScreen ? UIInterfaceOrientationMaskLandscapeRight : UIInterfaceOrientationMaskPortrait;
        UIWindowSceneGeometryPreferencesIOS *geometryPreferencesIOS = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:orientation];
        // 開始切換
        [scene requestGeometryUpdateWithPreferences:geometryPreferencesIOS errorHandler:^(NSError * _Nonnull error) {
            NSLog(@"強(qiáng)制%@錯(cuò)誤:%@", isLaunchScreen ? @"橫屏" : @"豎屏", error);
        }];
    } else {
        [self p_swichToNewOrientation:isLaunchScreen ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait];
    }
}

如果 Xcode 沒有更新到 14,需要采用委婉的方式進(jìn)行實(shí)現(xiàn)

/// 切換設(shè)備方向
/// - Parameter isLaunchScreen: 是否是全屏
- (void)p_switchOrientationWithLaunchScreen:(BOOL)isLaunchScreen {

    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    if (isLaunchScreen) {
        // 全屏操作
        appdelegate.launchScreen = YES;
    } else {
        // 退出全屏操作
        appdelegate.launchScreen = NO;
    }

    if (@available(iOS 16.0, *)) {
        void (^errorHandler)(NSError *error) = ^(NSError *error) {
            NSLog(@"強(qiáng)制%@錯(cuò)誤:%@", isLaunchScreen ? @"橫屏" : @"豎屏", error);
        };
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        SEL supportedInterfaceSelector = NSSelectorFromString(@"setNeedsUpdateOfSupportedInterfaceOrientations");
        [self performSelector:supportedInterfaceSelector];
        NSArray *array = [[UIApplication sharedApplication].connectedScenes allObjects];
        UIWindowScene *scene = (UIWindowScene *)[array firstObject];
        Class UIWindowSceneGeometryPreferencesIOS = NSClassFromString(@"UIWindowSceneGeometryPreferencesIOS");
        if (UIWindowSceneGeometryPreferencesIOS) {
            SEL initWithInterfaceOrientationsSelector = NSSelectorFromString(@"initWithInterfaceOrientations:");
            UIInterfaceOrientationMask orientation = isLaunchScreen ? UIInterfaceOrientationMaskLandscapeRight : UIInterfaceOrientationMaskPortrait;
            id geometryPreferences = [[UIWindowSceneGeometryPreferencesIOS alloc] performSelector:initWithInterfaceOrientationsSelector withObject:@(orientation)];
            if (geometryPreferences) {
                SEL requestGeometryUpdateWithPreferencesSelector = NSSelectorFromString(@"requestGeometryUpdateWithPreferences:errorHandler:");
                if ([scene respondsToSelector:requestGeometryUpdateWithPreferencesSelector]) {
                    [scene performSelector:requestGeometryUpdateWithPreferencesSelector withObject:geometryPreferences withObject:errorHandler];
                }
            }
        }
#pragma clang diagnostic pop
    } else {
        [self p_swichToNewOrientation:isLaunchScreen ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait];
    }
}
橫豎屏切換.gif

接下來,講解一下關(guān)于視頻的橫豎屏切換

場(chǎng)景:豎屏狀態(tài),視頻只是占了屏幕的一小部分,橫屏需要鋪滿整個(gè)屏幕

視頻橫豎屏切換

先將 redView 占屏幕的一小部分,切換橫豎屏效果如下:

視頻橫豎屏切換.gif

監(jiān)聽橫豎屏狀態(tài)

在這里橫屏狀態(tài)需要鋪滿整個(gè)屏幕,在 iOS16 之前監(jiān)聽 UIDeviceOrientationDidChangeNotification 通知,在監(jiān)聽方法中進(jìn)行對(duì) redView 的約束更新,但是在 iOS16 開始,這個(gè)通知并不會(huì)一定有(我這邊有時(shí)候有,有時(shí)候沒有通知過來),在找了一遍官方文檔之后,發(fā)現(xiàn) UIViewControlleriOS8 開始就有一個(gè)方法 viewWillTransitionToSize:withTransitionCoordinator:

/* 
 This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized). 
 當(dāng) UIViewController 的 view 大小被其父級(jí)更改時(shí)(即當(dāng)根控制器的窗口旋轉(zhuǎn)或調(diào)整大小時(shí)),將調(diào)用此方法
 If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
 */
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));

于是在這里就不需要監(jiān)聽 UIDeviceOrientationDidChangeNotification 通知了

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    NSLog(@"view 發(fā)生改變:%@", NSStringFromCGSize(size));
    BOOL isLaunchScreen = NO;
    if (@available(iOS 16.0, *)) {
        // iOS16 需要使用 UIWindowScene 來區(qū)分橫豎屏
        NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
        UIWindowScene *scene = [array firstObject];
        isLaunchScreen = scene.interfaceOrientation == UIInterfaceOrientationLandscapeRight;
    } else {
        // 這里是 UIDeviceOrientationLandscapeLeft(我們需要 Home 按鍵在右邊)
        // UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
        isLaunchScreen = [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft;
    }

    NSLog(@"將要%@", isLaunchScreen ? @"橫屏" : @"豎屏");
}

打印信息:

<主>-[YXCLaunchScreenController viewWillTransitionToSize:withTransitionCoordinator:] 47行 : view 發(fā)生改變:{812, 375}
<主>-[YXCLaunchScreenController viewWillTransitionToSize:withTransitionCoordinator:] 57行 : 將要橫屏
<主>-[YXCLaunchScreenController viewWillTransitionToSize:withTransitionCoordinator:] 47行 : view 發(fā)生改變:{375, 812}
<主>-[YXCLaunchScreenController viewWillTransitionToSize:withTransitionCoordinator:] 57行 : 將要豎屏

適配 redView

根據(jù)當(dāng)前屏幕方向?qū)?redView 進(jìn)行適配,豎屏占屏幕一部分空間,橫屏占滿整個(gè)屏幕

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
    NSLog(@"view 發(fā)生改變:%@", NSStringFromCGSize(size));
    BOOL isLaunchScreen = NO;
    if (@available(iOS 16.0, *)) {
        NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
        UIWindowScene *scene = [array firstObject];
        isLaunchScreen = scene.interfaceOrientation == UIInterfaceOrientationLandscapeRight;
    } else {
        // 這里是 UIDeviceOrientationLandscapeLeft(我們需要 Home 按鍵在右邊)
        // UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
        isLaunchScreen = [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft;
    }
    
    NSLog(@"將要%@", isLaunchScreen ? @"橫屏" : @"豎屏");
    [self p_updateViewWithIsLaunchScreen:isLaunchScreen size:size];
}
/// 適配橫豎屏約束
/// - Parameters:
///   - isLaunchScreen: 是否是橫屏
///   - size: 當(dāng)前控制器 View 的 size 大小
- (void)p_updateViewWithIsLaunchScreen:(BOOL)isLaunchScreen size:(CGSize)size {

    if (isLaunchScreen) {
        // 橫屏
        [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.edges.equalTo(self.view);
        }];
    } else {
        // 豎屏
        [self.redView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.center.left.right.equalTo(self.view);
            make.height.mas_equalTo(150);
        }];
    }
}

最后的效果如圖:

視頻橫豎屏切換最后效果.gif

以上就是關(guān)于 iOS16 橫豎屏適配以及關(guān)于視頻適配的全部?jī)?nèi)容

代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,870評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,527評(píng)論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評(píng)論 1 322
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,656評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,853評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,103評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,487評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,815評(píng)論 2 372

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

  • 一、監(jiān)聽橫豎屏的切換 1、通知方式: 如果使用這個(gè)通知,當(dāng)iPhone/iPad旋轉(zhuǎn)的時(shí)候,你會(huì)得到的旋轉(zhuǎn)方向會(huì)是...
    CholMay閱讀 12,786評(píng)論 6 33
  • 前言 現(xiàn)在大部分的智能移動(dòng)設(shè)備通過自動(dòng)旋轉(zhuǎn),能夠自動(dòng)切換去呈現(xiàn)最適合當(dāng)前屏幕顯示的內(nèi)容,無疑大大提升了使用者的用戶...
    BladeWayne閱讀 10,017評(píng)論 2 54
  • 目錄 一、最讓人糾結(jié)的三種枚舉 二、兩種屏幕旋轉(zhuǎn)的觸發(fā)方式 三、屏幕旋轉(zhuǎn)控制的優(yōu)先級(jí) 四、開啟屏幕旋轉(zhuǎn)的全局權(quán)限 ...
    來鬧的閱讀 2,911評(píng)論 0 4
  • 目錄一、最讓人糾結(jié)的三種枚舉二、兩種屏幕旋轉(zhuǎn)的觸發(fā)方式三、屏幕旋轉(zhuǎn)控制的優(yōu)先級(jí)四、開啟屏幕旋轉(zhuǎn)的全局權(quán)限五、開啟屏...
    Faner_NG閱讀 4,836評(píng)論 2 18
  • 一、什么是SizeClasses SizeClasses 是從iOS 8開始,Apple在應(yīng)用界面的可視化設(shè)計(jì)上添...
    XueYongWei閱讀 1,590評(píng)論 1 6