iOS開發——ios11、iphoneX適配實記

編譯器升級xcode9,準備適配一下iphoneX的布局,調試時發現由于一些ios11新特性或者底層變化的原因,xcode9運行出來的項目界面有bug,下面做一下新特性適配和iphoneX布局適配的記錄。

ios11新特性

UIScrollView新特性
UITbaleView自動增加了內邊距.jpeg
  • 現象:如上圖所示,圖中中間部分是一個表格,在ios11手機上,表格的頭部多出了一部分空隙。
  • 原因:ios11后,UIScrollView增加了新特性,多了一個屬性:contentInsetAdjustmentBehavior,理解為“調整內邊距的行為”,可選四個參數:
    -- UIScrollViewContentInsetAdjustmentAutomatic : 自動調整;
    -- UIScrollViewContentInsetAdjustmentScrollableAxes : 在滾動方向上調整;
    -- UIScrollViewContentInsetAdjustmentNever : 從不調整;
    -- UIScrollViewContentInsetAdjustmentAlways : 總是調整。
    默認是UIScrollViewContentInsetAdjustmentAutomatic,所以它自動增加了一部分空隙去避免遮擋(自動調整的判斷,包括:是否在導航控制器中、是否在tabbarViewController中等等)。
  • 解決方法:
if (@available(iOS 11.0, *))//表示只在ios11以上的版本執行
{
    _brandTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
正常時效果.png
UISearchBar新特性
UISearchBar的bug.jpeg
  • 現象:如圖所示,ios11這個searchBar莫名肥了一圈,我代碼沒變過,同樣設的是高度44,在ios10上是下面這個樣子


    正常狀態的searchBar.jpeg
  • 原因:UISearchBar的底層結構變了,簡單來看,以前的UISearchBar是下面這個樣子
    searchBar結構示意.png

    簡單來看,它由一個大的紅色view+內部輸入框(白色部分)構成,在ios11以前,高度設置控制的是大的紅色view的高度,內部輸入框(白色部分)的高度永遠固定是28
    而ios11后,它沒了大的紅色view,只有內部輸入框(白色部分),給高度的時候,自然就控制的是內部輸入框(白色部分)的高度。
    所以同樣都是44的高,ios11上的UISearchBar會肥一圈。
  • 解決方法:
    如果需要跟以前保持同樣的效果,那么在ios11上把UISearchBar的高度設為28并重新布局
UITableView使用MJRefreshAutoNormalFooter上拉刷新時頁面會跳動
  • 現象:正常情況:應該是觸發了上拉刷新,加載數據,加載完后增加若干cell,但頁面仍保持在原位置。而軟件在xcode9編譯環境下的ios11手機上,加載完后頁面會向上跳一截,尤其在cell的高度是由model的實際數據決定,尤其在cell高度較大的時候,很明顯。MJRefresh做了更新,但實測,還是有問題。
  • 原因:UITableView這兩個代理方法調用順序變了:cellForRowAtIndexPath和heightForRowAtIndexPath,直接導致了獲取高度算tableview偏移有問題。
  • 解決辦法:
    實現以下方法:
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 200;
}

盡量給出較接近實際cell高度的數值,跳動問題會消失。

  • 2018.02.01更新,不需要實現estimatedHeightForRowAtIndexPath這個代理,設置下面三個參數即可
    if (@available(iOS 11.0, *))//表示只在ios11以上的版本執行
    {
        tableview.estimatedRowHeight = 0;
        tableview.estimatedSectionHeaderHeight = 0;
        tableview.estimatedSectionFooterHeight = 0;
    }
導航欄返回按鈕偏移、自定義按鈕難以點中
返回按鈕偏移bug.jpeg
  • 現象:UINavigationController的navBar的返回按鈕,我隱藏了它的文字,結果它比在ios10上,明顯向右向下偏移了一截(不隱藏文字時不會有這種問題)。我自定義了一個左邊按鈕作為返回按鈕,發現非常難點中。
  • 原因:底層實現機制改變。
  • 解決辦法:
    自定義左邊按鈕作為返回按鈕
    那么如何解決自定義按鈕難以點中的問題呢?
    給navBar添加一個單擊手勢,判斷手勢位置,如果在左邊某個范圍,就調用按鈕的點擊事件。
    我是給UIViewController寫了一個分類UIViewController+YYBackBarButton,這樣需要用的頁面里,只需要在viewDidLoad中加下面一句話就行了
[self addCustomBackButtonWithBackButtonColor:UIColorFromRGB(0x979797)];

分類.m文件代碼如下:

#import "UIViewController+YYBackBarButton.h"
#import "UIImage+extend.h"

@implementation UIViewController (YYBackBarButton)

- (void)addCustomBackButtonWithBackButtonColor:(UIColor *)backButtonColor
{
    self.navigationController.navigationBar.tintColor = [UIColor clearColor];//返回按鈕顏色
    self.navigationController.navigationBar.backItem.hidesBackButton = YES;//隱藏返回按鈕
    self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;//開啟手勢右滑返回
    
    //創建返回按鈕
    UIButton *backButton = [[UIButton alloc] init];
    backButton.frame = CGRectMake(0, 0, 13, 22);
    [backButton setImage:[[UIImage imageNamed:@"backButton.png"] imageWithColor:backButtonColor] forState:UIControlStateNormal];//imageWithColor這個方法是UIImage+extend分類里的方法,作用是對圖片的顏色進行自定義渲染(說白了就是改圖片顏色)
    backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
    //返回按鈕偏移值,根據自己的需要設置偏移,已達到想要的UI效果
    CGFloat insetNumber = 0;
    if (@available(iOS 11.0, *))
    {
        if (kMainScreenHeight == IPHONE_4_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_5_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_6_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_6PLUS_SCREEN_HEIGHT) insetNumber = 12;
        else if (kMainScreenHeight == IPHONE_X_SCREEN_HEIGHT) insetNumber = 8;
    }
    else
    {
        if (kMainScreenHeight == IPHONE_4_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_5_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_6_SCREEN_HEIGHT) insetNumber = 8;
        else if (kMainScreenHeight == IPHONE_6PLUS_SCREEN_HEIGHT) insetNumber = 12;
        else if (kMainScreenHeight == IPHONE_X_SCREEN_HEIGHT) insetNumber = 8;
    }
    [backButton setImageEdgeInsets:UIEdgeInsetsMake(0, -insetNumber, 0, insetNumber)];
    
    //添加按鈕事件
    [backButton addTarget:self action:@selector(customClickBackButton) forControlEvents:UIControlEventTouchUpInside];

    //添加手勢,獲取點擊,避免不容易點中返回按鈕的問題
    [self.navigationController.navigationBar addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickNavBar:)]];
    
    //把按鈕設置為導航欄左邊按鈕
    UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:backButton];
    self.navigationItem.leftBarButtonItem = leftBarButtonItem;
}

- (void)customClickBackButton
{
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)clickNavBar:(UITapGestureRecognizer *)tap
{
    //獲取手勢的點
    CGPoint tapPoint = [tap locationInView:self.navigationController.navigationBar];
    if (tapPoint.x <= kMainScreenWidth * 0.15)//如果手勢的點在左邊15%,就讓它出發返回按鈕事件
    {
        [self customClickBackButton];
    }
}

@end

其中有個圖片重新渲染顏色的方法,我寫在了UIImage+extend這個分類中,可以改一張圖片的整體顏色,方法實現如下:

- (UIImage *)imageWithColor:(UIColor *)color
{
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextClipToMask(context, rect, self.CGImage);
    [color setFill];
    CGContextFillRect(context, rect);
    UIImage*newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

這樣處理之后的效果,就跟以前一樣了:


解決后效果.jpeg
鍵盤新特性
IQKeyboardManager鍵盤工具欄bug.jpeg
  • 現象:我在項目里使用IQKeyboardManager框架,正常情況下,紅圈處左邊應該有上下跳轉輸入框的按鈕、右邊應該有完成按鈕。
  • 原因:ios11鍵盤window底層布局變化。
  • 解決辦法:
    更新IQKeyboardManager框架(研究了半天原因才發現人家已經更新了做好適配了)。


    正常鍵盤工具欄.jpeg
XCode9運行訪問系統相冊崩潰問題
保存圖片崩潰.jpeg
  • 現象:如圖保存圖片功能,在XCode9下運行會崩潰
  • 原因:info.plist新增了權限配置
  • 解決:info.plist新增一條權限:Privacy - Photo Library Additions Usage Description


    新增權限.jpeg

iphoneX適配

主要是UI方面的適配,但也發現了一個不明原因的bug。

bug的解決

iphoneX上的bug.gif
  • 現象:注意觀察上圖中tabbar的位置。從帶tabbar的控制器push到其它控制器(hidesBottomBarWhenPushed = YES),tabbar在push的時候會突然向上移動一截,但是pop的時候又是正常的,并且同樣是ios11的iphone8上都是正常的。
  • 原因:目前不清楚是不是xcode9模擬器的問題,也不知道真機上會不會有。
  • 解決:重寫- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated方法,自己控制push時tabbar的frame
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // 重寫super
    [super pushViewController:viewController animated:animated];

    // 修改tabBra的frame
    if (isIPhoneX)//解決push時tabbar瞬間上移的問題
    {
        CGRect frame = self.tabBarController.tabBar.frame;
        frame.origin.y = [UIScreen mainScreen].bounds.size.height - frame.size.height;
        self.tabBarController.tabBar.frame = frame;
    }
}

即可恢復正常狀態。

UI適配

主要是statusBar(頂部狀態欄)的高度不一樣了、底部增加了一個安全距離。適配思路就是避開這些地方,具體思路下文分開介紹。

頂部狀態欄的問題
頂部遮擋.png
  • 現象:如圖所示,頂部的searchView被擋住了一塊。
  • 原因:這個頁面的頂部白色部分是一個view,它的高度我寫死為64(普通手機里導航欄+狀態欄整體高度),所以在普通手機里看起就是一個正常的導航欄,而在這里就發生了遮擋,因為普通手機的狀態欄高度是20而iphoneX是44
  • 解決:所有這種頂部的view,都不應該寫死64的高度,而應該寫成導航欄高度+狀態欄高度
    導航欄高度所有都是44,沒有變化,而狀態欄高度分為20和44,布局時判斷一下是不是iphoneX,數值也隨之變化,最好是寫成宏。
    頂部遮擋的解決.png
底部安全距離的問題
iphoneX上tabbar.png
  • 現象:如上圖所示,tabbar的高度明顯比普通手機里高得多,增加的區域大概是下圖紅圈的區域:
    自動增加的區域.jpeg

    這部分的高度,即底部安全距離的高度是34
    總所周知,底部出現安全距離,是為了給系統的手勢讓路,用戶在底部黑條附近進行拖動時,可以達到原來home鍵的效果。
    然而,通過我在模擬器上的反復實驗,系統手勢的觸發范圍,并沒有34這么高,而是從黑條頂部到屏幕底部這個范圍,這部分的高度,以下稱作系統手勢有效范圍,高度為13。
  • 實際問題:上圖是系統處理的tabbar高度。在自己寫的頁面里,就會出現遮擋的問題:


    底部遮擋效果.jpeg
  • 解決:底部兩個按鈕背后有個白色view,它的高度是寫死為54,應該改為54+手勢有效范圍高度,手勢有效范圍高度根據是不是iphoneX賦值為13或0,這樣就完美適配了iphoneX和普通手機,解決效果如下:


    底部遮擋問題解決.jpeg

備注

1、干貨分享:對于手機型號的判斷、底部高度、頂部高度等,我都寫成了宏,需要的朋友拿走不謝:

//是否是手機
#define isIPhone (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
//是否是iphoneX
#define isIPhoneX (kMainScreenWidth >= 375.0f && kMainScreenHeight >= 812.0f && isIPhone)
//蘋果X寬高
#define IPHONE_X_SCREEN_WIDTH 375
#define IPHONE_X_SCREEN_HEIGHT 812
//底部安全高度
#define BOTTOM_SAFE_HEIGHT (isIPhoneX ? 34 : 0)
//系統手勢高度
#define SYSTEM_GESTURE_HEIGHT (isIPhoneX ? 13 : 0)
//tabbar高度
#define TABBAR_HEIGHT (49 + BOTTOM_SAFE_HEIGHT)
//狀態欄高度
#define STATUS_HEIGHT (isIPhoneX ? 44 : 20)
//導航欄高
#define NAVBAR_HEIGHT 44
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容