iOS 11 & iPhoneX 適配

1、safeArea

  1. automaticallyAdjustsScrollViewInsets to contentInsetAdjustmentBehavior 在iOS 11中,蘋果廢棄了 UIViewControllerautomaticallyAdjustsScrollViewInsets屬性,改用UIScrollViewcontentInsetAdjustmentBehavior 屬性來替換它。
// OC
@property(nonatomic,assign) BOOL automaticallyAdjustsScrollViewInsets API_DEPRECATED_WITH_REPLACEMENT("Use UIScrollView's contentInsetAdjustmentBehavior instead", ios(7.0,11.0),tvos(7.0,11.0)); // Defaults to YES

//swift
@available(iOS, introduced: 7.0, deprecated: 11.0)
open var automaticallyAdjustsScrollViewInsets: Bool // Defaults to YES
複製代碼

若是你的工程中沒有添加 iPhone X 對應的啓動圖,你會發現頂部和底部的一部分都是黑色的,那部分就是安全域。 想要對 iPhone X 作屏幕適配,第一步先要添加啓動圖。html

image.png

2、UITableView 自動計算高度

在iOS 11中,UITableViewestimatedRowHeightestimatedSectionHeaderHeightestimatedSectionFooterHeight 默認都是開啓的。真是使人驚喜。react

@property (nonatomic) CGFloat rowHeight;             // default is UITableViewAutomaticDimension
@property (nonatomic) CGFloat sectionHeaderHeight;   // default is UITableViewAutomaticDimension
@property (nonatomic) CGFloat sectionFooterHeight;   // default is UITableViewAutomaticDimension
@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable
@property (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable
@property (nonatomic) CGFloat estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable
複製代碼

踩坑1

在咱們的app中,首頁的 UITableView 列表是手動計算的行高,忽然出現了以往只有開啓自動計算行高才會出現的滾動條跳動的現象,給 TableViewcontentSize 加了observer,果真一直在變。而後在這裏找到了緣由。android

踩坑2 (更新)

在使用 MJRefersh 的時候,自己代碼存在一個 bug,原來並無發現,iOS 11 幫我發現了這個問題。ios

// tableView 設置
let footer = MJRefreshAutoFooter(refreshingBlock: {  [weak self] in
    if let strongSelf = self {
        if let block = strongSelf.footerBlock {
            block()
        }
    }
})
footer?.triggerAutomaticallyRefreshPercent = -20
tableView.mj_footer = footer

// 請求結束後處理(錯誤)
models.append(requestModels)
if models.count < pageSize { // 這裏是個bug
    tableView.mj_footer.endRefreshingWithNoMoreData()
} else {
    tableView.mj_footer.state = .idle
}
tableView.reloadData()

// 請求結束後處理(正確)
if requestModels.count < pageSize { // 修復bug
    tableView.mj_footer.endRefreshingWithNoMoreData()
} else {
    tableView.mj_footer.state = .idle
}
models.append(requestModels)
tableView.reloadData()
複製代碼

上面代碼中,在 iOS 11 以前也存在隱含的問題,就是 tableView footer 一直不能被標記爲 無更多數據 的狀態,即便沒有數據了,用戶下拉依而後發起請求。 在 iOS 11 中,因爲估算行高,互動到底部時,依然會觸發 tableViewcontentSizecontentOffset 變化,同時會觸發 refreshingBlock, 和 tableViewreloadData,致使了最後一頁數據陷入了循環請求。swift

引伸問題 若是存在使用監聽 tableViewcontentSizecontentOffset 變化來觸發的一些事件,最好把 tableView 的自動計算行高關掉,以避免出現問題。react-native

3、iPhone X***(嚴重)***

1. 狀態欄獲取網絡狀態

衆所周知,iPhone X多了個「美美的」劉海,它的狀態欄發生了變化。這也致使了原來從狀態欄視圖獲取網絡狀態的API出了問題,會致使閃退。xcode

// 獲取當前app狀態欄子視圖
UIApplication *app = [UIApplication sharedApplication];
NSArray *children = [[[app valueForKeyPath:@"statusBar"] valueForKeyPath:@"foregroundView"] subviews];
int type = 0;
for (id child in children) {
    if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
        type = [[child valueForKeyPath:@"dataNetworkType"] intValue];
    }
}

switch (type) {
    case 1:
        return @"2G";
    case 2:
        return @"3G";
    case 3:
        return @"4G";
    case 5:
        return @"WIFI";
    default:
        return @"Unknow";
        break;
}
複製代碼

不過你依然能夠用AFNetworking相似的方式,獲取網絡狀態:安全

SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);

switch (self.networkReachabilityAssociation) {
    case AFNetworkReachabilityForName:
        break;
    case AFNetworkReachabilityForAddress:
    case AFNetworkReachabilityForAddressPair:
    default: {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
            SCNetworkReachabilityFlags flags;
            SCNetworkReachabilityGetFlags(self.networkReachability, &flags);
            AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
            dispatch_async(dispatch_get_main_queue(), ^{
                callback(status);

                NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
                [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }];


            });
        });
    }
        break;
}

static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) {
    BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
    BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0);
    BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0));
    BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0);
    BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction));

    AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown;
    if (isNetworkReachable == NO) {
        status = AFNetworkReachabilityStatusNotReachable;
    }
#if TARGET_OS_IPHONE
    else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
        status = AFNetworkReachabilityStatusReachableViaWWAN;
    }
#endif
    else {
        status = AFNetworkReachabilityStatusReachableViaWiFi;
    }

    return status;
}
複製代碼

2.狀態欄

  1. 狀態欄高度由原來的20,變爲44了,之前使用常量的就被坑了。
  2. 在修改咱們的宏過程當中遇到了另一個坑,特此提醒:
// 原有
#define MPStatusBarHeight (20)
// 新的
#define MPStatusBarHeight (UIApplication.sharedApplication.statusBarFrame.size.height)

/* 坑在此處 */
// application 的 statusBarFrame 在隱藏狀態欄的時候是 CGRectZero
@property(nonatomic,readonly) CGRect statusBarFrame __TVOS_PROHIBITED; // returns CGRectZero if the status bar is hidden
複製代碼

3.友盟分享

友盟分享 SDK 6.4.6 版本提供的界面沒有適配 iPhone X 須要更新 SDK 到最新的 6.8.0 版本,很遺憾的是,目前最新的友盟全部產品都沒有提供 cocoapods 版本,須要手動集成。 在集成過程當中遇到一個坑: 因爲咱們工程同時使用 pod 引用了友盟分享和友盟統計,原本只想升級友盟分享,因而直接跳到集成友盟分享的文檔部分,執行了下面的操做:bash

一、移除pod對 UMShare的引用,執行 pod install
二、添加UMShareSDK到工程
三、修改文件引用錯誤
四、清除DerivedData
五、在模擬器build
複製代碼

報錯: 網絡

image.png

詢問了友盟客服(立刻就回復了,很給力),是因爲沒有集成 common 基礎庫,回過頭來看文檔,發現集成友盟任意庫都須要加入 common 基礎庫。 可是一旦加入 common 基礎庫,又會和使用 pod 引用的友盟統計衝突,無奈,只能統計庫和分享庫都換成手動引用。

4、UIToolBar

UIToolBar 中兩側添加了 UIBarButtonItem,在 iOS 11 以前會佈局在邊,在 iOS 11 中兩個item是緊挨着的,須要在中間添加 UIBarButtonSystemItemFlexibleSpace 類型的 item 才能夠。

5、ALAssetsLibrary 保存圖片閃退

在 iOS 11 以前,你能夠直接使用下面代碼來保存圖片到相冊:

#import <AssetsLibrary/AssetsLibrary.h>
// ALAssetsLibrary
[[[ALAssetsLibrary alloc] init] writeImageDataToSavedPhotosAlbum:imageData metadata:nil completionBlock:^(NSURL *assetURL, NSError *error) {
    [self saveResultWithResult:(error == nil)];
}];
複製代碼

在 iOS 11中則會直接崩潰,保存圖片的新姿式:

  • 解決方案1: 在 infoPlist 文件中追加相冊寫入權限的提示
參數 key Xcode name 版本 說明
NSPhotoLibraryAddUsageDescription "Privacy - Photo Library Additions Usage Description" Specifies the reason for your app to get write-only access to the user’s photo library. See NSPhotoLibraryAddUsageDescription for details. iOS 11 and later 本參數iOS 11必須追加
NSPhotoLibraryUsageDescription 「Privacy - Photo Library Usage Description」 Specifies the reason for your app to access the user’s photo library. See NSPhotoLibraryUsageDescription for details. iOS 6.0 and later 本參數iOS 10之後必須追加

在infoPlist中直接添加(xcode 中 open as Source Code):

<key>NSPhotoLibraryUsageDescription</key>
<string>此 App 須要您的贊成才能讀取媒體資料庫</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>此 App 須要您的贊成才能寫入媒體資料庫</string>
複製代碼
  • 解決方案2: 若是沒有按方案1添加寫入權限提示,ALAssetsLibrary 寫入直接崩潰,而 PHPhotoLibrary 保存圖片會直接觸發向用戶請求相冊寫入權限的 Alert 提示。
#import <Photos/Photos.h>
// PHPhotoLibrary
- (void)saveImageByPHPhotoLibrary:(UIImage *)image {
    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        [PHAssetCreationRequest creationRequestForAssetFromImage:image];
    } completionHandler:^(BOOL success, NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self saveResultWithResult:(success && error == nil)];
        });
    }];
}
複製代碼

更新

因爲在應用中使用了 WKWebView , WKWebView 在沒有處理的狀況下,長按圖片會彈出保存圖片的選項,選擇保存圖片一樣會崩潰。因此該方案不能處理好全部入口,推薦使用方案1。

image.png

6、鍵盤鑰匙串 (password auto fill)

一、問題

在iOS 11系統中,你會發現你本來的一些輸入框喚起系統鍵盤後出現以下的情況:

image.png

鍵盤右上角的🔑,是iOS 11的新特性 Password Auto Fill,假如你在代碼中設置 UITextField 的 contentType 爲 usernamepassword類型,就會出現上面的圖標。

usernameTextField.textContentType = UITextContentType.username
passwordTextField.textContentType = UITextContentType.password
複製代碼

而有些狀況,在 XIB 文件中,若是你沒有指定特定類型,鍵盤也會出現🔑:

image.png

對應狀況你只須要將 contentType 設置爲其它類型便可:

image.png

二、關於 Password Auto Fill

Password Auto Fill 的適配能夠參考iOS 11 ---- Password Auto Fill (PS: 感受機制有點相似UniversalLink)

7、react-native

能夠參考下面代碼,來適配狀態欄和底部安全域:

import { Dimensions, Platform } from 'react-native'

const { width, height } = Dimensions.get('window')

export const isIphoneX = () => (
    Platform.OS === 'ios' &&
    !Platform.isPad &&
    !Platform.isTVOS &&
    (height === 812 || width === 812)
)

const getStatusBarHeight = () => {
  if (Platform.OS === 'android') {
    return 0
  }

  if (isIphoneX()) {
    return 44
  }

  return 20
}

// const
export const IPHONEX_BOTTOM_HEIGHT = 34
export const STATUSBAR_HEIGHT = getStatusBarHeight()
複製代碼
相關文章
相關標籤/搜索