iOS11 与 iPhone X适配的那些坑(持更中...)
目錄 問題列表
1.適配iPhoneX 屏幕原則
2.適配過程一些常量的設置
3..iPhone X 上運行有黑色區域問題
4.iOS11導航欄適配?
5.出現UIScrollview 漂移問題(基本都是iPhoneX上)
6.UITableView 出現的遮擋顯示問題(三行代碼解決,放到基類)
7.iOS11 UICollectionview 的scroll indicator等被組視圖遮擋的問題
8.一些關于iPhone X底部的設計示范
9.關于iPhoneX的home條(HomeIndicator)的顯隱
10.有些導致iPhoneX獲取網絡狀態崩潰的問題
11.關于iOS11權限的變化
12.XCode9添加資源文件不能獲取路徑問題
13.iOS11 無法彈出鍵盤問題
14.iOS11 數組語法糖?替換方法的改變
1. 適配iPhoneX 屏幕原則
圖5 iphoneX 豎屏 圖6 iPhoneX 橫屏設計原則:讓那行可操作、不可阻擋的控件或視圖顯示在圖5、圖6的藍色區域(安全區域safeArea)。
示范圖一?
示范圖二?
示范圖三頂部危險區距離:44
底部危險區距離:34
2. 適配過程一些常量的設置
#define IS_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) //狀態欄高度 #define kStatusBarHeight (CGFloat)(IS_iPhoneX?(44):(20)) // iPhoneX的狀態欄高度差值 #define kTopBarDifHeight (CGFloat)(IS_iPhoneX?(24):(0)) // 頂部安全區域遠離高度 #define kTopBarSafeHeight (CGFloat)(IS_iPhoneX?(44):(0)) // 狀態欄和導航欄總高度 #define kNavBarHAbove (CGFloat)(IS_iPhoneX?(88):(64)) // TabBar高度 #define kTabBarHeight (CGFloat)(IS_iPhoneX?(49+34):(49)) // 底部安全區域遠離高度 #define kBottomSafeHeight (CGFloat)(IS_iPhoneX?(34):(0))3.?iPhone X 上運行有黑色區域問題
啟動圖問題,使用LaunchScreen來做啟動圖 或者 修改Assets中的LaunchImage,添加iPhoneX的Launch圖1125*2436(px) 豎屏模式。
.
通過LaunchScreen.storyboard方式啟動
如果使用的是Assets中的LaunchImage, 在增加了iPhone X尺寸的圖片配置后.
LaunchScreen.storyboard方式不用多說, 這里說一下如何在LaunchImage中增加iPhone X尺寸的圖片配置.
方法一:
準備一張尺寸:1125 * 2436的 3x啟動圖片, 移動到LaunchImage的Finder目錄中, 并在LaunchImage中的Contents.json文件中增加 (注意Json格式):
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "2436h",
"filename" : "圖片名.png",
"minimum-system-version" : "11.0",
"orientation" : "portrait",
"scale" : "3x"
}
?
?
方法二:
?
?
?
?
?
?
2.引導頁iPhone X圖片變形
?
?
?
?
由于iPhone X高度發生變化,圖片鋪滿整個屏幕時候造成圖片拉伸,現在需要UI切一個1125*2436的3x圖片和以前做iPhone X機型判斷1124*2001圖片,并且對圖片contentMode屬性進行設置
if (IS_iPhoneX) {
coverImageNames = [NSArray arrayWithObjects:@"img_big_1_ipx.jpg", @"img_big_2_ipx.jpg",@"img_big_3_ipx.jpg", @"img_big_4_ipx.jpg",nil];
}else{
coverImageNames = [NSArray arrayWithObjects:@"img_big_1.jpg", @"img_big_2.jpg",@"img_big_3.jpg", @"img_big_4.jpg",nil];
}
imageView.clipsToBounds = YES;//超出區域裁剪
imageView.contentMode = UIViewContentModeScaleAspectFill;//圖片等比例拉伸,會填充整個區域,但是會有一部分過大而超出整個區域
4、導航欄
4.1在解釋導航欄變化之前先解釋一個iOS 11的新特性:設置大標題,通過BOOL類型的prefersLargeTitles屬性來設置。默認設置是不開啟
//顯示大標題“設置”self.navigationController.navigationBar.prefersLargeTitles = YES;self.navigationItem.title = @"設置";//通過navigationItem的largeTitleDisplayMode屬性來設置:self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAutomatic;//其中LargeTitle還有三種樣式可以選擇:UINavigationItemLargeTitleDisplayModeNever//總是顯示小標題UINavigationItemLargeTitleDisplayModeAlways//總是顯示大標題UINavigationItemLargeTitleDisplayModeAutomatic//自動顯示大標題或小標題。即初始時是大標題,滑動時大標題隱藏、顯示小標題。4.2. iOS11 系統導航欄上自定義view的顯示問題
之前代碼設置導航欄:
self.navigationItem.titleView = customView;會出現這樣的問題,如下圖3,
圖3 導航欄出錯界面出亂的原因是因為iOS11調整了導航欄圖層結構。快速簡便的修改方式,直接上代碼,
self.navigationItem.hidesBackButton = YES;[self.navigationController.navigationBaraddSubview:YourCustomView];
但是需在控制器的dealloc里移除你的自定義view,
[YourCustomView removeFromSuperview];?
下圖4為修改好的自定義視圖
圖4(1)在iPhone X中導航欄高度變成88,項目中自定義導航欄高度64,需要對導航欄高度進行判斷
_navigationBar = [[MCCustomNavigationBar alloc]initWithFrame:CGRectMake(0, 0, SCREENW, 64) andType:CustomBarType_Home];
_navigationBar = [[MCCustomNavigationBar alloc]initWithFrame:CGRectMake(0, 0, SCREENW, kStatusBarAndNavigationBarHeight) andType:CustomBarType_Home];
(2)搜索框和左右兩邊按鈕高度有問題,因為iPhone X中狀態欄高度由20增加24變成44,所以搜索框和左右兩邊按鈕高度在iPhone X上距頂部高度加上24高度,
#define itemTopOffset (IS_iPhoneX ? (26+24) : 26)
[self.leftBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.offset(itemHorOffset);
make.height.equalTo(@(itemHeight));
make.width.equalTo(@(itemHeight));
make.top.offset(itemTopOffset);
}];
(3)iOS11上searchBar高度明顯變大,
UISearchBar改變搜索框的高度
系統的searchBar
UISearchBar的中子控件及其布局
UIView(直接子控件) frame 等于 searchBar的bounds,view的子控件及其布局
UISearchBarBackground(間接子控件) frame 等于searchBar的bounds
UISearchBarTextField(間接子控件) frame.origin等于(8.0, 6.0),即不等于searchBar的bounds
改變searchBar的frame只會影響其中搜索框的寬度,不會影響其高度,原因如下:
系統searchBar中的UISearchBarTextField的高度默認固定為28
左右邊距固定為8,上下邊距是父控件view的高度減去28除以2
改變UISearchBar的高度
方案
重寫UISearchBar的子類(MCSearchBar),重新布局UISearchBar子控件的布局
增加成員屬性contentInset,控制UISearchBarTextField距離父控件的邊距
若用戶沒有設置contentInset,則計算出默認的contentInset
若用戶設置了contentInset,則根據最新的contentInset布局UISearchBarTextField
新建UISearchBar的子類,增加成員屬性contentInset,用來調整UISearchBarTextField距離父控件的邊距。contentInset的setter方法
#pragma mark - setter method
- (void)setContentInset:(UIEdgeInsets)contentInset {
_contentInset.top = contentInset.top;
_contentInset.bottom = contentInset.bottom;
_contentInset.left = contentInset.left;
_contentInset.right = contentInset.right;
self.isChangeFrame = YES;
[self layoutSubviews];
}
- (void)layoutSubviews {
[super layoutSubviews];
for (UIView *subView in self.subviews[0].subviews) {
if ([subView isKindOfClass:[UIImageView class]]) {
//移除UISearchBarBackground
[subView removeFromSuperview];
}
if ([subView isKindOfClass:[UITextField class]]) {
CGFloat height = self.bounds.size.height;
CGFloat width = self.bounds.size.width;
if (_isChangeFrame) {
//說明contentInset已經被賦值
// 根據contentInset改變UISearchBarTextField的布局
subView.frame = CGRectMake(_contentInset.left, _contentInset.top, width - 2 * _contentInset.left, height - 2 * _contentInset.top);
} else {
// contentSet未被賦值
// 設置UISearchBar中UISearchBarTextField的默認邊距
CGFloat top = (height - 28.0) / 2.0;
CGFloat bottom = top;
CGFloat left = 8.0;
CGFloat right = left;
_contentInset = UIEdgeInsetsMake(top, left, bottom, right);
subView.frame = CGRectMake(_contentInset.left, _contentInset.top, width - 2 * _contentInset.left, height - 2 * _contentInset.top);
}
}
}
}
在項目中使用自定義的MCSearchBar,SearchBar的高度可以改變
(4)iOS11中UISearchBar沒有居中居左顯示,并且icon和placeholder間距太窄
現在實現居中顯示@property(nonatomic,assign)CGFloatwidth;
//判斷版本
if(@available(iOS11.0, *)){
UITextField *textField = [self.searchBar valueForKey:@"searchField"];
[textField sizeToFit];
//記錄一下這個時候的寬度
_width= textField.frame.size.width;
[_searchBar setPositionAdjustment:UIOffsetMake((_searchBar.width-_width)/2.0,0)forSearchBarIcon:UISearchBarIconSearch];
}
然后在代理方法 searchBarTextDidBeginEditing:(UISearchBar*)searchBar 調整位置
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar{
if (@available(iOS 11.0, *)) {
if(!_searchBar.text.length) {
[_searchBar setPositionAdjustment:UIOffsetMake(0,0)forSearchBarIcon:UISearchBarIconSearch];
}
}
}
結束的方法? searchBarTextDidEndEditing:(UISearchBar*)searchBar 判斷是否有內容
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar{
if (@available(iOS 11.0, *)) {
if(!_searchBar.text.length) {
[_searchBar setPositionAdjustment:UIOffsetMake((_searchBar.width-self.width)/2.0,0) forSearchBarIcon:UISearchBarIconSearch];
}
}
}
如果有占位文字后臺返的,UITextField在搜索框默認文字大小17,可以根據文字個數和大小算出占位文字寬度,然后算出搜索圖標的偏移量.
?
(5)搜索頁面導航欄中搜索框距離返回按鈕太近
?
?
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, testView.width - (SCREENW > 320 ? 90: 100)*SCREEN_Proportion, testView.height)];
由于搜索框距離左邊距離導致的,現在需要定義一個宏做判斷
#define marginLeft (IS_iPhoneX ? 10 : 0)
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(marginLeft, 0, testView.width - (SCREENW > 320 ? 90: 100)*SCREEN_Proportion, testView.height)];
?
?
5. 出現UIScrollview 漂移問題(基本都是iPhoneX上)
若是UIScrollview的frame和contentSize高度或者寬度一致卻還導致了漂移問題,可用以下一行代碼搞定。
if(@available(iOS11,*)) {_scrollView.contentInsetAdjustmentBehavior=UIScrollViewContentInsetAdjustmentNever;}同時,如果在push進入webview頁面時,底部會有黑邊一閃而過,也可用此方法解決。
若嫌麻煩,每個scrollview及其子類都要去設置的話,也可以使用以下方法
// AppDelegate 里進行全局設置 if(@available(iOS 11.0, *)){[[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];?
?
?
?
?
四:UITableview UICollectionView MJRefresh下拉刷新錯亂
iOS11表格用MJRefresh框架下拉刷新的時候界面會閃,顯示紊亂,
原因是iOS11棄用了automaticallyAdjustsScrollViewInsets屬性,新增contentInsetAdjustmentBehavior來替代它
tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
解決方法:MJRefresh作者已經針對iOS 11和iPhone X做了適配,把MJRefresh更新最新的版本
//聲明tableView的位置 添加下面代碼
if (@available(iOS 11.0, *)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
_tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);
_tableView.scrollIndicatorInsets = _tableView.contentInset;
}
六.純代碼適配底部工具欄
在iPhone X中底部工具欄需要為home indicator留出34pt邊距,iPhone X以前機型TarBar高度49,iPhone X中Tarbar高度加上34變成83.由于項目中Tabbar是用自定義的,需要對Tabbar的高度做適配,用到宏#define kTabbarHeight (IS_iPhoneX ? (49.f+34.f) : 49.f)
?
?
?
適配前代碼
_customBarView = [[UIView alloc] initWithFrame:CGRectMake(0, SCREENH - 49,SCREENW, 49)];
適配后的代碼_customBarView = [[UIView alloc] initWithFrame:CGRectMake(0, SCREENH - kTabbarHeight,SCREENW, kTabbarHeight)];
?
?
圖標和文字和其他機型相比偏下,現在需要對圖標和文字高度進行調整,做iPhone X和非iPhone X機型適配
?
?
?
?
?
七.底部view出現問題
?
(1)購物車View適配,涉及頁面商品詳情頁,搜索頁面,購物車頁面
?
?
?
?
?
?
?
(2)自定義數字鍵盤適配
?
?
?
適配底部用的一個宏#define? kTabbarSafeBottomMargin? ? ? ? (IS_iPhoneX ? 34.f : 0.f)用于計算距離屏幕頂部高度,如果iPhone X,留出距離屏幕底部34安全距離,控件不能顯示34安全距離上
?
適配前代碼
?
適配后代碼
?
(3)篩選views適配
?
適配前
?
適配后
?
?九.自定義分割線處理
1.iOS 11分割線顏色加深
iOS 11中有的自定義分割線顏色莫名奇妙變深,很難看,如下圖所示,不符合設計規范,嘗試其他方法沒有解決這個問題,最后通過改變分割線的透明度來解決
?
適配前
適配后
2.沒有文字顯示分割線
// 這些界面以下使用代理方法來設置,發現并沒有生效
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
// 這樣的原理是因為之前只是實現了高度的代理方法,卻沒有實現View的代理方法,iOS10及以前這么寫是沒問題的,iOS11開啟了行高估算機制引起的bug,因此有以下幾種解決方法:
// 解決方法一:添加實現View的代理方法,只有實現下面兩個方法,方法 (CGFloat)tableView: heightForFooterInSection: 才會生效
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return nil;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return nil;
}
// 解決方法二:直接使用tableView屬性進行設置,修復該UI錯亂
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 5;
[_optionTableView setContentInset:UIEdgeInsetsMake(-35, 0, 0, 0)];
// 解決方法三:添加以下代碼關閉估算行高
3. UITableView 出現的遮擋顯示問題(三行代碼解決,放到基類)
self.tableView.estimatedRowHeight = 0;self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
8.有些導致iPhoneX獲取網絡狀態崩潰的問題
直接上代碼,無需通過KVC得到圖層名獲取網絡狀態,一勞永逸!(因還不熟悉markdown語法,以下代碼需自行縮進
#import <CoreTelephony/CTTelephonyNetworkInfo.h>+ (NSString *)getNetWorkInfo {
? ? NSString *strNetworkInfo = @"No Network";
? ? struct sockaddr_storage zeroAddress;
? ? bzero(&zeroAddress,sizeof(zeroAddress));
? ? zeroAddress.ss_len = sizeof(zeroAddress);
? ? zeroAddress.ss_family = AF_INET;
// Recover reachability flags
? ? SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL,(struct sockaddr *)&zeroAddress);
? ? SCNetworkReachabilityFlags flags;
//獲得連接的標志? ?
? ? BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability,&flags);
? ? CFRelease(defaultRouteReachability);
//如果不能獲取連接標志,則不能連接網絡,直接返回
? ? if(!didRetrieveFlags){ return strNetworkInfo;} BOOL isReachable = ((flags & kSCNetworkFlagsReachable)!=0);BOOL needsConnection = ((flags & kSCNetworkFlagsConnectionRequired)!=0);if(!isReachable || needsConnection) {return strNetworkInfo;} // 網絡類型判斷
? ? if((flags & kSCNetworkReachabilityFlagsConnectionRequired)== 0){strNetworkInfo = @"WIFI";}? ?
? ? if(((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0) { if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0){strNetworkInfo = @"WIFI";}}
? ? if ((flags & kSCNetworkReachabilityFlagsIsWWAN) ==kSCNetworkReachabilityFlagsIsWWAN) {if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {CTTelephonyNetworkInfo * info = [[CTTelephonyNetworkInfo alloc] init];NSString *currentRadioAccessTechnology = info.currentRadioAccessTechnology;if (currentRadioAccessTechnology) {if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {strNetworkInfo =? @"4G";} else if ([currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] || [currentRadioAccessTechnologyisEqualToString:CTRadioAccessTechnologyGPRS]) {strNetworkInfo =? @"2G";} else {strNetworkInfo =? @"3G";}}} else {if((flags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable) {if ((flags & kSCNetworkReachabilityFlagsTransientConnection) == kSCNetworkReachabilityFlagsTransientConnection) {if((flags & kSCNetworkReachabilityFlagsConnectionRequired) == kSCNetworkReachabilityFlagsConnectionRequired) {strNetworkInfo = @"2G";} else {strNetworkInfo = @"3G";}}}}}
? ? // if ([strNetworkInfo isEqualToString: @"No Network"]) {strNetworkInfo = @"WWAN";}
? ? return strNetworkInfo;
}
?10. 關于iPhoneX的home條(HomeIndicator)的顯隱
先看下圖7,
圖7 橫屏看撩妹視頻時這樣的體驗其實是不好的(還怎么用英文撩妹?),所以為了以防這樣的問題出現,蘋果其實提供了隱藏HomeIndicator的方法,如下,
- (BOOL)prefersHomeIndicatorAutoHidden {? return YES;
}
在VC 里邊重寫 prefersHomeIndicatorAutoHidden 返回 YES(默認是NO),Home指示條就能自動隱藏了,此方法是在屏幕若無交互事件響應時,延遲2秒左右會回調此方法。
11. 關于iOS11權限的變化
1.定位問題:無法定位,第一次使用時沒有彈出定位請求對話框。
原因:由于iOS11的定位權限的key做了更改,在以前iOS11之前的始終允許定位NSLocationAlwaysUsageDescription基礎上添加NSLocationWhenInUseUsageDescription和NSLocationAlwaysAndWhenInUsageDescription。具體參考博客:http://blog.csdn.net/dangyalingengjia/article/details/77965029
2.相冊權限問題:iOS11上讀寫相冊的照片時發生崩潰。
原因:由于iOS11相冊相關權限的key發生了變化。用戶在沒有權限的情況下,訪問相冊導致崩潰。
iOS11之前對應的key是NSPhotoLibraryUsageDescription,而iOS11的Key變為NSPhotoLibraryAddUsageDescription。同定位的Key一樣,由于key沒有兼容性,所以需要保留原key以兼容iOS11之前的版本。
12. iOS11 UICollectionview 的scroll indicator等被組視圖遮擋的問題
問題如下圖8紅框區域,
圖8?UICollectionview bug圖導致這個的原因是因為組視圖的z坐標出現了問題, 有以下兩種解決辦法
第一種: 在該類中重寫layoutSubviews方法<適用于小規模>
- (void)layoutSubviews {? ? [super layoutSubviews];
? ? self.layer.zPosition = 0;
}
第二種: 自定義一個layer, 重寫其zPosition的get方法。然后對組視圖類添加分類方法,重寫layerClass方法即可。
自定義layer:
@implementation YYZPositionZeroLayer- (CGFloat)zPosition {
? ? return 0;
}
@end
添加分類重寫組視圖layerClass方法:
@implementationUICollectionReusableView (ZPositionZero)+ (Class)layerClass {
? return[YYZPositionZeroLayer class];
}
@end
12. XCode9添加資源文件不能獲取路徑問題
XCode9調用[[NSBundle mainBundle] pathForResource:resourceNameofType:nil];方法來獲取文件路徑,返回為nil. 添加正確方式如下圖1,
圖1? 添加資源選項但是XCode9 卻埋了一道坑,如下圖2, 點擊資源文件,找到文件選項
圖2? 資源文件選項紅框區域未默認勾選, 只要把這個復選框勾選即可解決.? 具體原因是:xcode9 添加文件使用addfile 拖拽的文件不會自動添加到Compile Sources 和 Copy Bundle Resources 下.
13. iOS11 無法彈出鍵盤問題
問題描述: 在某些時候你會發現調用becomeFirstResponder方法時,并未響應鍵盤,系統鍵盤無法彈出.
問題發現: 如果在調用之前使用了UIAlertView彈出對話框,就會導致系統鍵盤無法彈出,猜測是因為UIAlertView在消失遍歷window窗口時存在windowLevel 大于 levelNormal 的window把他作為keywindow
問題解決: 如果你的項目里面還在使用UIAlertView(2_0,9_0), 建議用UIAlertController替換掉.
14.iOS11 數組語法糖?替換方法的改變
在iOS11 之前,
array[i] 語法糖的替換方法是[array objectAtIndex:i]
在iOS11 之后,
array[i] 語法糖的替換方法是[array objectAtIndexedSubscript:i]
用到動態交換方法的童鞋需要注意
free(properties);
}
?
轉載于:https://www.cnblogs.com/edensyd/p/8418021.html
總結
以上是生活随笔為你收集整理的iOS11 与 iPhone X适配的那些坑(持更中...)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql5.6.4以下不支持多个字段类
- 下一篇: 怎样调整XenServer下面Linux