iOS16 橫豎屏切換適配

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

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

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

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

AppDelegate

AppDelegate 的 .h 文件中添加一個(gè)變量來(lái)記錄是否需要進(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

接下來(lái)在需要切換橫豎屏的 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)過(guò)以上代碼,就能實(shí)現(xiàn)在 iOS16 之前的設(shè)備上進(jìn)行橫豎屏切換,下面開(kāi)始適配 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];
        // 開(kāi)始切換
        [scene requestGeometryUpdateWithPreferences:geometryPreferencesIOS errorHandler:^(NSError * _Nonnull error) {
            NSLog(@"強(qiáng)制%@錯(cuò)誤:%@", isLaunchScreen ? @"橫屏" : @"豎屏", error);
        }];
    } else {
        [self p_swichToNewOrientation:isLaunchScreen ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait];
    }
}

如果 Xcode 沒(méi)有更新到 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

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

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

視頻橫豎屏切換

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

視頻橫豎屏切換.gif

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

在這里橫屏狀態(tài)需要鋪滿整個(gè)屏幕,在 iOS16 之前監(jiān)聽(tīng) UIDeviceOrientationDidChangeNotification 通知,在監(jiān)聽(tīng)方法中進(jìn)行對(duì) redView 的約束更新,但是在 iOS16 開(kāi)始,這個(gè)通知并不會(huì)一定有(我這邊有時(shí)候有,有時(shí)候沒(méi)有通知過(guò)來(lái)),在找了一遍官方文檔之后,發(fā)現(xiàn) UIViewControlleriOS8 開(kāi)始就有一個(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)聽(tīng) 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 來(lái)區(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、監(jiān)聽(tīng)橫豎屏的切換 1、通知方式: 如果使用這個(gè)通知,當(dāng)iPhone/iPad旋轉(zhuǎn)的時(shí)候,你會(huì)得到的旋轉(zhuǎn)方向會(huì)是...
    CholMay閱讀 12,948評(píng)論 6 33
  • 前言 現(xiàn)在大部分的智能移動(dòng)設(shè)備通過(guò)自動(dòng)旋轉(zhuǎn),能夠自動(dòng)切換去呈現(xiàn)最適合當(dāng)前屏幕顯示的內(nèi)容,無(wú)疑大大提升了使用者的用戶...
    BladeWayne閱讀 10,265評(píng)論 2 54
  • 目錄 一、最讓人糾結(jié)的三種枚舉 二、兩種屏幕旋轉(zhuǎn)的觸發(fā)方式 三、屏幕旋轉(zhuǎn)控制的優(yōu)先級(jí) 四、開(kāi)啟屏幕旋轉(zhuǎn)的全局權(quán)限 ...
    來(lái)鬧的閱讀 3,082評(píng)論 0 4
  • 目錄一、最讓人糾結(jié)的三種枚舉二、兩種屏幕旋轉(zhuǎn)的觸發(fā)方式三、屏幕旋轉(zhuǎn)控制的優(yōu)先級(jí)四、開(kāi)啟屏幕旋轉(zhuǎn)的全局權(quán)限五、開(kāi)啟屏...
    Faner_NG閱讀 5,008評(píng)論 2 18
  • 關(guān)于橫豎屏適配,有一句說(shuō)一句,坑挺深的。之前做Vision和畢設(shè)的時(shí)候就處理過(guò)橫豎屏問(wèn)題,不過(guò)當(dāng)時(shí)的功力太淺,明顯...
    HarwordLiu閱讀 37,590評(píng)論 26 137

友情鏈接更多精彩內(nèi)容