iOS11及iPhoneX適配

蘋(píng)果公司于9月份如期發(fā)布了新的iPhone-iPhone8,iPhone8 Plus,iPhoneX,前兩個(gè)不用多說(shuō),正常形態(tài)的iPhone和前代外觀上沒(méi)有太大區(qū)別。iPhoneX則帶來(lái)了不同的樣式,不同的體驗(yàn),18:9的全面屏屏幕,小劉海,去掉Home鍵后超前的交互方式。當(dāng)然對(duì)于開(kāi)發(fā)者也帶來(lái)了對(duì)于這塊屏幕的適配問(wèn)題。我想蘋(píng)果爸爸決定在11月初開(kāi)售iPhoneX也有一部分讓開(kāi)發(fā)者對(duì)自己的App做iPhoneX適配的一部分原因,畢竟現(xiàn)在Xcode已經(jīng)有iPhoneX的模擬器了。

過(guò)去,我們拿到的手機(jī)是方方正正的矩形,所以整個(gè)屏幕都可以看做是安全區(qū)域Safe Area,而如今由于iPhone X屏幕上的“劉?!币约捌聊凰闹懿捎脠A角的設(shè)計(jì),需要設(shè)計(jì)師對(duì)繪圖區(qū)域做出調(diào)整。蘋(píng)果給出的安全區(qū)域如下

image ![](http://upload-images.jianshu.io/upload_images/1346171-d08f0e41eb1c5c77.gif?imageMogr2/auto-orient/strip)

頁(yè)面內(nèi)容不能超出安全區(qū)域(Safe Area)

image

下面我們以通訊錄和News應(yīng)用為例看下iPhoneX模擬器中原生應(yīng)用對(duì)于這塊全面屏如何適配的

iPhoneX System App.gif

通過(guò)例子我們可以發(fā)現(xiàn)主要的三點(diǎn)原則:

  1. 帶有空間按鈕的頂部導(dǎo)航欄(NavigationBar)要處在“劉?!毕旅?/li>
  2. 底部導(dǎo)航欄(Tabbar)不能在虛擬橫條Home鍵(不知道咋叫,暫且這么叫吧)下面,也就是說(shuō)要和屏幕底部保持距離
  3. 可滾動(dòng)的列表整塊屏幕都是可展示的,但是滾動(dòng)條要和頂部和底部保持距離不能超出

基本以上三點(diǎn)原則可以概括為一句話,所有不可滾動(dòng)的控件推薦在安全區(qū)域內(nèi)展示,可滾動(dòng)的控件整個(gè)屏幕都可以用來(lái)展示

這么做也算是充分利用了這塊屏幕,并且不影響用戶正常使用iPhoneX了

Toon的適配

初期Toon對(duì)于iPhoneX的適配基本為0,所以出現(xiàn)不少問(wèn)題,主要集中以下幾點(diǎn):

  1. 頂部導(dǎo)航欄和底部導(dǎo)航欄超出安全區(qū)域
  2. 沒(méi)有導(dǎo)航欄的列表沒(méi)有全屏展現(xiàn)
  3. 吸底按鈕超出安全區(qū)域
  4. 頂部導(dǎo)航欄UI錯(cuò)亂
  5. 列表加載控件在安全區(qū)域外部展示

下面通過(guò)一個(gè)Gif圖來(lái)看下未經(jīng)過(guò)適配的Toon的部分界面在iPhoneX上表現(xiàn)

iPhoneX System App1.gif

可見(jiàn)未經(jīng)適配的Toon將會(huì)以16:9的樣子展現(xiàn)在用戶手中,這對(duì)于產(chǎn)品在iPhoneX中的體驗(yàn)來(lái)說(shuō)將會(huì)是極大的災(zāi)難,沒(méi)有充分利用iPhoneX的全面屏,用戶體驗(yàn)將是缺失的

經(jīng)過(guò)一段時(shí)間的適配,現(xiàn)在開(kāi)發(fā)版的Toon在iPhoneX上已經(jīng)可以有良好的展示了,雖然還有很多地方?jīng)]有經(jīng)過(guò)重新設(shè)計(jì)和優(yōu)化,不過(guò)已經(jīng)利用了iPhoneX的屏幕展示了

iPhoneX System App2.gif

經(jīng)過(guò)一段時(shí)間的適配,解決了16:9展示,導(dǎo)航欄錯(cuò)位等問(wèn)題,在的問(wèn)題主要集中在列表底部加載控件的問(wèn)題等問(wèn)題上,接下來(lái)本文將通過(guò)Demo和Toon的部分界面來(lái)具體講一下iPhoneX UI適配上的問(wèn)題

啟動(dòng)頁(yè)的適配

如果對(duì)于啟動(dòng)頁(yè)不做任何適配,那么App啟動(dòng)后你會(huì)發(fā)現(xiàn)應(yīng)用是16:9的樣式展示的

解決方案有兩種:

  1. Xib或者Stroyboard來(lái)作為應(yīng)用的啟動(dòng)圖
  2. 添加iPhoneX下啟動(dòng)頁(yè)的圖片

Toon工程中采取的方案是第二種

屏幕快照 2017-09-26 下午11.53.29.png

頂部的適配

以前通過(guò)加減20來(lái)覆蓋或者避免狀態(tài)的代碼都會(huì)出問(wèn)題在iPhoneX上

image

狀態(tài)欄高度不是20了,iOS11安全區(qū)的提出,在iPhoneX上狀態(tài)欄的高變?yōu)?4
代碼中需要通過(guò)[UIApplication sharedApplication].statusBarFrame.size.height獲取狀態(tài)欄高度

[self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(self.view.mas_top).offset([UIApplication sharedApplication].statusBarFrame.size.height);
    make.bottom.equalTo(self.view.mas_bottom);
    make.left.equalTo(self.view.mas_left);
    make.right.equalTo(self.view.mas_right);
}];
image

iOS11automaticallyAdjustsScrollViewInsets屬性廢棄了會(huì)出現(xiàn)ScorllView下沉20的現(xiàn)象

image

可以調(diào)用scrollview新的apicontentInsetAdjustmentBehavior

self.automaticallyAdjustsScrollViewInsets = NO;
if (@available(iOS 11.0, *)) {
    self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
image

但是這么寫(xiě)會(huì)導(dǎo)致在iPhoneX下出現(xiàn),由于在X下安全區(qū)域的出現(xiàn),頂部異形區(qū)域不建議覆蓋,會(huì)造成視覺(jué)的差異

image

在代碼中我們需要來(lái)根據(jù)設(shè)備高度來(lái)判斷iPhoneX,從而來(lái)避免這種情況

[self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
    if (CGRectGetHeight([UIScreen mainScreen].bounds) == 812.0) {
        if (@available(iOS 11.0, *)) {
            make.top.equalTo(self.view.mas_safeAreaLayoutGuideTop);
        }
    } else {
        make.top.equalTo(self.view.mas_top);
    }
    make.bottom.equalTo(self.view.mas_bottom);
    make.left.equalTo(self.view.mas_left);
    make.right.equalTo(self.view.mas_right);
}];
image

如果用了MJRefresh在iPhoneX下列表頂部會(huì)出現(xiàn)這樣的情況,頂部刷新控件會(huì)有露出,UI不美觀

image

如果設(shè)置contentInsetAdjustmentBehaviorUIScrollViewContentInsetAdjustmentNever,并且設(shè)置頂部距離為導(dǎo)航欄距離,又會(huì)造成全面屏展示不充分也不是很好

- (void)viewDidLoad {
    [super viewDidLoad];
    <!--省略部分代碼-->
    if (@available(iOS 11.0, *)) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
    <!--省略部分代碼-->
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    [self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(self.view.layoutMargins.top);
        <!--省略部分代碼-->
    }];
}
image

我建議的適配方式,根據(jù)具體情況來(lái)設(shè)置contentInset的值

- (void)viewDidLoad {
    [super viewDidLoad];
    <!--省略部分代碼-->
    if (@available(iOS 11.0, *)) {
        self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    }
    <!--省略部分代碼-->
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    if (CGRectGetHeight([UIScreen mainScreen].bounds) == 812.0) {
        // 只在iPhoneX下適配
        if (@available(iOS 11.0, *)) {
            self.tableView.contentInset = UIEdgeInsetsMake(self.view.safeAreaInsets.top, 0, 0, 0);
        }
    }
    [self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(0);
        <!--省略部分代碼-->
    }];
}

使用以上代碼,或者UI設(shè)計(jì)頂部刷新控件都樣式都可以解決該問(wèn)題,但是我覺(jué)得最終極的解決方案還是UI設(shè)計(jì)根據(jù)iPhoneX的異性全面屏給以良好的適配方案,如果有更好的設(shè)計(jì)方案,比如當(dāng)列表為初始滾動(dòng)狀態(tài)時(shí)不顯示頂部刷新控件,可以跟我交流

image

頂部的適配問(wèn)題主要集中體現(xiàn)在以前通過(guò)寫(xiě)死狀態(tài)高度20造成的,對(duì)于這個(gè)問(wèn)題,只要調(diào)用系統(tǒng)提供獲取狀態(tài)欄高度的方法,就可以避免,至于頂部刷新控件的問(wèn)題,這個(gè)本文建議采取和本文建議的處理底部加載控件的方案來(lái)實(shí)施,具體可以繼續(xù)看下文

底部的適配

底部導(dǎo)航欄

如果是采用系統(tǒng)默認(rèn)的底部導(dǎo)航欄,沒(méi)有采用自定義的方式,底部導(dǎo)航欄iOS系統(tǒng)級(jí)就做了處理,會(huì)保證在Tabbar是在安全區(qū)域之內(nèi)

如果是采取自定義的方式那么則要對(duì)做出響應(yīng)的處理

+ (CGFloat)computeTabbarHeight {
    NSInteger style = [[TNAppStackManager shareInstance] rootStyle];
    if (style == RootControllerStyle_TabCircleDrawer || style == RootControllerStyle_TabCircleNoDrawer) {
        return 70.;
    } else if (style == RootControllerStyle_TabNormal) {
        return [[UIDevice currentDevice] systemVersion].doubleValue >= 11.0 ? (fabs(CGRectGetHeight([UIScreen mainScreen].bounds) - 812.) >= 1.0 ? 49. : 83.) : 53.;
    }
    
    return 0;
}

以上是Toon工程在處理底部導(dǎo)航欄高度的示例代碼,通過(guò)系統(tǒng)版本和設(shè)備來(lái)判斷具體導(dǎo)航欄的高度

列表底部加載控件的的處理

列表的底部加載控件和在全屏下的頂部刷新控件的問(wèn)題是我認(rèn)為不不好給出解決方案的問(wèn)題

iOS11廢棄了原有的automaticallyAdjustsScrollViewInsets屬性,為scrollview添加了新的屬性
contentInsetAdjustmentBehavior

現(xiàn)在對(duì)于我列表的適配,我看大都是這個(gè)樣子的

self.automaticallyAdjustsScrollViewInsets = NO;
if (@available(iOS 11.0, *)) {
    self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;

這兩個(gè)屬性都是為了讓列表對(duì)于全屏和異形屏幕下有良好的展示設(shè)計(jì)的,對(duì)于非全屏狀態(tài)下的列表,這兩個(gè)屬性不處理沒(méi)有關(guān)系,因?yàn)橹挥性谌粱蛘甙肴粒ㄖ挥许敳繉?dǎo)航或者)

對(duì)于Toon來(lái)說(shuō),剛開(kāi)我們對(duì)于所有的列表都將上面的屬性置為了UIScrollViewContentInsetAdjustmentNever,這樣在iPhoneX部分界面就變成這樣了

iPhoneX示例
iPhoneX示例

會(huì)發(fā)現(xiàn)在iPhoneX下,tableView展示區(qū)域是到底的,這樣會(huì)影響用戶使用home虛擬橫條,所以這個(gè)值是需要根據(jù)具體情況分析的,例如如果tableView是全屏展示的就需要設(shè)置為UIScrollViewContentInsetAdjustmentNever

在此基礎(chǔ)上需要適配就是tableView的刷新控件和加載控件了,假設(shè)大家使用的都是MjRefresh,那么對(duì)于刷新控件出現(xiàn)的問(wèn)題上文已經(jīng)講過(guò)了,不在贅述。我們來(lái)討論下加載控件會(huì)出現(xiàn)的問(wèn)題。

刷新控件還好,大部分刷新控件都是在有頂部導(dǎo)航欄的情況下,可是底部加載控件不同,又很多處理方式,本文只做一個(gè)拋磚引玉的示例,具體處理方式還是要結(jié)合產(chǎn)品、UI、技術(shù)來(lái)以前討論針對(duì)具體情況具體分析,接下來(lái)我將會(huì)以Toon中小組模塊我的評(píng)論界面為例,給出我的解決方案

如果contentInsetAdjustmentBehavior設(shè)置為UIScrollViewContentInsetAdjustmentNever,那么出現(xiàn)的問(wèn)題是,底部加載控件會(huì)在安全區(qū)域意外露出。

Simulator Screen Shot - iPhone X - 2017-09-29 at 00.06.07.png

為了明顯我講底部加載控件的背景色置成了橙色,可以看到正常情況,加載控件是暴露在安全區(qū)域外部,上面的文字也能看到,這樣一來(lái)既不沒(méi)關(guān)也顯得不夠?qū)I(yè),并且文字也被home虛擬橫條擋住了

那么怎么處理這種情況才會(huì)更好些呢,本文給的解決方案是給底部加載控件加一個(gè)遮罩,而這個(gè)遮罩是根據(jù),tableView的偏移量來(lái)展示的,最后的效果如下。

iPhoneX System App4.gif

核心代碼如下:

- (void)setLoadFooter {
    self.tableView.mj_footer = [MJRefreshBackStateFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadCommentData)];
    self.tableView.mj_footer.backgroundColor = [UIColor orangeColor];
    self.tableView.mj_footer.maskView = [[UIView alloc] init];
    self.tableView.mj_footer.maskView.backgroundColor = [UIColor whiteColor];
    [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (object == self.tableView && [keyPath isEqualToString:@"contentOffset"]) {
        if (@available(iOS 11.0, *)) {
            /*
             判斷設(shè)備為iPhoneX時(shí),
             并且contentInsetAdjustmentBehavior不為UIApplicationBackgroundFetchIntervalNever
             */
            if (CGRectGetHeight([UIScreen mainScreen].bounds) == 812.0 && self.tableView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
                CGFloat distanceToSafeBottom = (self.tableView.contentOffset.y + CGRectGetHeight(self.tableView.frame) - self.view.safeAreaInsets.bottom) - self.tableView.contentSize.height;
                if (distanceToSafeBottom < 0) {
                    self.tableView.mj_footer.maskView.frame = CGRectZero;
                } else {
                    CGFloat showFooterHeight = distanceToSafeBottom;
                    if (showFooterHeight > CGRectGetHeight(self.tableView.mj_footer.bounds)) {
                        showFooterHeight = CGRectGetHeight(self.tableView.mj_footer.bounds);
                    }
                    if (self.tableView.mj_footer.state != MJRefreshStateRefreshing) {
                        self.tableView.mj_footer.maskView.frame = CGRectMake(0, 0, CGRectGetWidth(self.tableView.mj_footer.bounds), showFooterHeight);
                    }
                }
            }
        }
    }
}
最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評(píng)論 25 708
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,188評(píng)論 22 665
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,838評(píng)論 4 61
  • 走過(guò)多少路口,去過(guò)多少城市,瞧過(guò)多少人流,看過(guò)多少風(fēng)景。 一起度過(guò)無(wú)數(shù)節(jié)日,今卻應(yīng)一道風(fēng)景而停駐,不忘過(guò)去的年年...
    GTR1042閱讀 377評(píng)論 0 1
  • 早上涂口紅把口紅弄斷了,剛剛開(kāi)鎖把鎖芯拔出來(lái)了,這樣的大力士女孩還有誰(shuí)
    滄瀾魚(yú)閱讀 358評(píng)論 0 0

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