iOS16適配-屏幕旋轉

聲明:本文適配以iOS 16 bate 2為基準

背景

iOS 16在UIKIT上有了一些更改,廢棄掉了一些修改方式,比如屏幕的橫豎屏旋轉,這一塊之前有很多中處理方法,但是如果之前用的是基于UIDevice的,那在這次更新后就會遇到強制旋轉屏幕不成功,且有如下日志提示。

圖1

適配前提

在iOS 16中,我們遇到了頁面旋轉不成功的問題,最初的代碼采用如下形式

@try {
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        UIDeviceOrientation val = UIDeviceOrientationLandscapeLeft;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
} @catch (NSException *exception) {
    
} @finally {
    
}

但是在執行的時候,就會出現屏幕旋轉失敗,日志提示圖1的內容。

根據日志提示,我們可以看到,UIDevice.orientation這個形式已經不再被支持,此處其實核心點就在于,我們之前進行的setOrientation其實是用KVC的形式去修改UIDevice里的readonly對象orientation。

但是在iOS 16里不知道為什么這個方法被禁用了,但是在API里又沒有發現提示。

圖2

適配過程

當滿足適配前提的時候,我們現在看一下如何去進行修改。

根據現有提示,我們可以從圖1看到里面的提示中,UIWindowScene.requestGeometryUpdate(_:)為標準的Swift寫法,由此我們可以去追溯至UIWindowScene相應的官方文檔,我們可以得到如下提示

圖3

由此我們可以確定,此方法為iOS16中新增的了。

然后根據文檔中OC的鏈接也可以得到如下的方法requestGeometryUpdateWithPreferences:errorHandler:

圖4

但是在這個方法里,有個點一定要注意,里面需要傳入一個UIWindowSceneGeometryPreferences對象,這個也是本次新增的。

圖5

但是你在直接對這個對象初始化的時候會發現,這是一個空的抽象類,并且API中會有提示。

圖6

此處略坑,根據這個提示,這個對象一定會有派生類去進行初始化,那這個時候我們點開API,就可以得到如下兩個派生類。

圖7

那么點到相應的派生類中,我們終于找到了初始化的方法。

圖8

至此,整個新API的尋找過程就OK了,然后我們進行一下實際嘗試。

另外,在嘗試中發現 application:supportedInterfaceOrientationsForWindow: 代理方法在iOS16中會有調用不到的情況,這里我們之前進行了可用旋轉狀態的處理。

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

綜合以上情況,手動調用setNeedsUpdateOfSupportedInterfaceOrientations觸發一下application:supportedInterfaceOrientationsForWindow,我們可以用如下形式進行解決

if (@available(iOS 16.0, *)) {
    [vc.navigationController setNeedsUpdateOfSupportedInterfaceOrientations];
    
    NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
    UIWindowScene *ws = (UIWindowScene *)array[0];
    UIWindowSceneGeometryPreferencesIOS *geometryPreferences = [[UIWindowSceneGeometryPreferencesIOS alloc] init];
    geometryPreferences.interfaceOrientations = UIInterfaceOrientationMaskLandscapeLeft;
    [ws requestGeometryUpdateWithPreferences:geometryPreferences
        errorHandler:^(NSError * _Nonnull error) {
        //業務代碼
    }];
}

如果暫時不能用Xcode14,那么可以用如下形式解決,原理和上面是一樣的

if (@available(iOS 16.0, *)) {
    @try {
        NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
        UIWindowScene *ws = (UIWindowScene *)array[0];
        Class GeometryPreferences = NSClassFromString(@"UIWindowSceneGeometryPreferencesIOS");
        id geometryPreferences = [[GeometryPreferences alloc]init];
        [geometryPreferences setValue:@(UIInterfaceOrientationMaskLandscapeRight) forKey:@"interfaceOrientations"];
        SEL sel_method = NSSelectorFromString(@"requestGeometryUpdateWithPreferences:errorHandler:");
        void (^ErrorBlock)(NSError *err) = ^(NSError *err){
            //業務代碼
        };
        if ([ws respondsToSelector:sel_method]) {
            (((void (*)(id, SEL,id,id))[ws methodForSelector:sel_method])(ws, sel_method,geometryPreferences,ErrorBlock));
        }
    } @catch (NSException *exception) {
        //異常處理
    } @finally {
        //異常處理
    }
}

這樣就可以旋轉屏幕了。

具體的旋轉方向,我們可以修改UIWindowSceneGeometryPreferencesIOS中的interfaceOrientations屬性,這個屬性為頁面方向UIInterfaceOrientationMask枚舉。

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);

那么剩下的就是,根據我們自身不同的業務場景,進行兼容或者重構即可。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容