1. 程式人生 > >你可能需要為你的 APP 適配 iOS 11

你可能需要為你的 APP 適配 iOS 11

作者:sonialiu

導語:iOS 11 為整個生態系統的 UI 元素帶來了一種更加大膽、動態的新風格。 本文介紹iOS11中在UI方面做了哪些更新,有些更新可以為使用者提供更加完美的體驗,但也有的可能會給目前的APP帶來異常bug

前言

前幾天發現在做的APP在 iOS11 系統上動畫有異常,在其他系統的裝置上都是正常的,動畫的操作是觀察tableViewcontentOffset變化後執行的,異常動畫發生在tableView reloadData之後,也就是說tableView reloadData之後,tableViewcontentOffset發生了幾次變化。查了下資料發現原因是 iOS11 中預設開啟了Self-Sizing

,在WWDC 2017 session204 Updating Your App for iOS 11 中有介紹,因此研究了下這個session,本文作為一個總結,下文的第三部分會有對上述的動畫異常的原因分析及解決方式。

本文內容包括:集成了搜尋的大標題欄、橫向選項卡欄、Margins 和 Insets以及 UIScrollViewUITableView 的更新和功能更強大的滑動操作。

一. 在UIKit’s Bars中加入的新功能

WWDC通過iOS新增的檔案管理App:Files開始介紹,在Files這個APP中能夠看到iOS11中UIKit’s Bars的一些新特性:在瀏覽功能上的大標題檢視(向上滑動後標題會回到原來的UI效果)、橫屏狀態下tab上的文字和icon會變為左右排列。我用iOS11的模擬器體驗了一下Files這個APP的豎屏和橫屏,如下圖所示:

(command+向左的箭頭讓模擬器橫屏)

橫屏時,在iPhone上,tab上的圖示較小,tab bar較小,這樣垂直空間可多放置內容。如果有人看不清楚tab bar上的圖示或文字,可以通過長按tab bar上的任意item,會將該item顯示在HUD上,這樣可以清楚的看清icon和text。對tool bar 和 navigation bar同理,長按item也會放大顯示。如下圖顯示:

UIBarItem

UIBarItem是UI tab bar item和UI bar button item的父類,要想實現上面介紹的效果,只需要為UIBarItem 設定landscapeImagePhone

屬性,在storyboard中也支援這個設定,對於HUD的image需要設定另一個iOS11新增的屬性:largeContentSizeImage,關於這部分更詳細的討論,可以參考 WWDC2017 Session 215:What’s New in Accessibility

控制大標題的顯示

在UI navigation bar中新增了一個BOOL屬性prefersLargeTitles,將該屬性設定為ture,navigation bar就會在整個APP中顯示大標題,如果想要在控制不同頁面大標題的顯示,可以通過設定當前頁面的navigationItemlargeTitleDisplayMode屬性;

typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {  
/// 自動模式依賴上一個 item 的特性
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 針對當前 item 總是啟用大標題特性
UINavigationItemLargeTitleDisplayModeAlways,
/// Never 
UINavigationItemLargeTitleDisplayModeNever,
}

把你的UISearchController賦值給navigationItem,就可以實現將UISearchController整合到Navigation

navigationItem.searchController  //iOS 11 新增屬性
navigationItem.hidesSearchBarWhenScrolling //決定滑動的時候是否隱藏搜尋框;iOS 11 新增屬性

UINavigationController和滾動互動

滾動的時候,以下互動操作都是由UINavigationController負責調動的:

  1. UIsearchController搜尋框效果更新
  2. 大標題效果的控制
  3. Rubber banding效果 //當你開始往下拉,大標題會變大來回應那個滾輪

所以,如果你使用navigation bar,組裝push和pop體驗,你不會得到searchController的整合、大標題的控制更新和Rubber banding效果,因為這些都是由UINavigationController控制的。

UIToolbar and UINavigationBar— Layout

在 iOS 11 中,當蘋果進行所有這些新特性時,也進行了其他的優化,針對 UIToolbar 和 UINavigaBar 做了新的自動佈局擴充套件支援,自定義的bar button items、自定義的title都可以通過layout來表示尺寸。 需要注意的是,你的constraints需要在view內部設定,所以如果你有一個自定義的標題檢視,你需要確保任何約束只依賴於標題檢視及其任何子檢視。當你使用自動佈局,系統假設你知道你在做什麼。

Avoiding Zero-Sized Custom Views

自定義檢視的size為0是因為你有一些模糊的約束佈局。要避免檢視尺寸為0,可以從以下方面做:

  • UINavigationBar 和 UIToolbar 提供位置
  • 開發者則必須提供檢視的size,有三種方式:

    • 對寬度和高度的約束;
    • 實現 intrinsicContentSize;
    • 通過約束關聯你的子檢視;

二. 管理margins 和 insets

layout margins

基於約束的Auto Layout,使我們搭建能夠動態響應內部和外部變化的使用者介面。Auto Layout為每一個view都定義了marginmargin指的是控制元件顯示內容部分的邊緣和控制元件邊緣的距離。 可以用layoutMargins或者layoutMarginsGuide屬性獲得view的margin,margin是檢視內部的一部分。layoutMargins允許獲取或者設定UIEdgeInsets結構的marginlayoutMarginsGuide則獲取到只讀的UILayoutGuide物件。

在iOS11新增了一個屬性:directional layout margins,該屬性是NSDirectionalEdgeInsets結構體型別的屬性:

typedef struct NSDirectionalEdgeInsets {  
    CGFloat top, leading, bottom, trailing;
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

layoutMarginsUIEdgeInsets結構體型別的屬性:

typedef struct UIEdgeInsets {  
CGFloat top, left, bottom, right;
} UIEdgeInsets;

從上面兩種結構體的對比可以看出,NSDirectionalEdgeInsets 屬性用leading 和 trailing 取代了之前的 left 和 right。

directional layout margins屬性的說明如下:

directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL.

Vice versa for directionalLayoutMargins.trailing.

例子:當你設定了trailing = 30;當在一個right to left 語言下trailing的值會被設定在view的左邊,可以通過layout margins的left屬性讀出該值。如下圖所示:

還有其他一些更新。自從引入layout margins,當將一個view新增到viewController時,viewController會修復view的layoutMargins為UIKit定義的一個值,這些調整對外是封閉的。從iOS11開始,這些不再是一個固定的值,它們實際是最小值,你可以改變你的view的layoutMargins為任意一個更大的值。而且,viewController新增了一個屬性:viewRespectsSystemMinimumLayoutMargins,如果你設定該屬性為”false”,你就可以改變你的layout margins為任意你想設定的值,包括0,如下圖所示:

安全區域(Safe Area)

如下圖:照片應用程式

從iOS 7以來,我們在整個作業系統中都有這些半透明的bars,蘋果鼓勵我們通過這些bars繪製內容,我們是通過viewController 的edgesForExtendedLayout屬性來做這些的。

iOS 7 開始,在 UIViewController中引入的 topLayoutGuidebottomLayoutGuide 在 iOS 11 中被廢棄了,取而代之的就是safeArea的概念,safeArea是描述你的檢視部分不被任何內容遮擋的方法。 它提供兩種方式:safeAreaInsetssafeAreaLayoutGuide來提供給你safeArea的參照值,即 insets 或者 layout guide。 safeArea區域如下圖所示:

如果有一個自定義的viewController,你可能要新增你自己的bars,增加safeAreaInsets的值,可以通過一個新的屬性:addtionalSafeAreaInsets來改變safeAreaInsets的值,當你的viewController改變了它的safeAreaInsets值時,有兩種方式獲取到回撥:

UIView.safeAreaInsetsDidChange()
UIViewController.viewSafeAreaInsetsDidChange()

三. UIScrollView and UITableView的新特性

Scroll Views

如果有一些文字位於UI滾動檢視的內部,幷包含在導航控制器中,現在一般navigationContollers會傳入一個contentInset給其最頂層的viewController的scrollView,在iOS11中進行了一個很大的改變,不再通過scrollView的contentInset屬性了,而是新增了一個屬性:adjustedContentInset,下面的兩張圖的對比能夠表示adjustContentInset表示的區域:

新增的contentInsetAdjustmentBehavior屬性用來配置adjustedContentInset的行為,該結構體有以下幾種型別:

typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {  
    UIScrollViewContentInsetAdjustmentAutomatic, 
    UIScrollViewContentInsetAdjustmentScrollableAxes,
    UIScrollViewContentInsetAdjustmentNever,
    UIScrollViewContentInsetAdjustmentAlways,
}
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;
//adjustedContentInset值被改變的delegate
- (void)adjustedContentInsetDidChange; 
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;

Table Views :在iOS 11中預設啟用Self-Sizing

這個應該是UITableView最大的改變。我們知道在iOS8引入Self-Sizing 之後,我們可以通過實現estimatedRowHeight相關的屬性來展示動態的內容,實現了estimatedRowHeight屬性後,得到的初始contenSize是個估算值,是通過estimatedRowHeight x cell的個數得到的,並不是最終的contenSizetableView就不會一次性計算所有的cell的高度了,只會計算當前螢幕能夠顯示的cell個數再加上幾個,滑動時,tableView不停地得到新的cell,更新自己的contenSize,在滑到最後的時候,會得到正確的contenSize。在測試Demo中,建立tableView到顯示出來的過程中,contentSize的計算過程如下圖:

Self-Sizing在iOS11下是預設開啟的,Headers, footers, and cells都預設開啟Self-Sizing,所有estimated 高度預設值從iOS11之前的 0 改變為UITableViewAutomaticDimension

@property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // default is UITableViewAutomaticDimension, set to 0 to disable

如果目前專案中沒有使用estimateRowHeight屬性,在iOS11的環境下就要注意了,因為開啟Self-Sizing之後,tableView是使用estimateRowHeight屬性的,這樣就會造成contentSize和contentOffset值的變化,如果是有動畫是觀察這兩個屬性的變化進行的,就會造成動畫的異常,因為在估算行高機制下,contentSize的值是一點點地變化更新的,所有cell顯示完後才是最終的contentSize值。因為不會快取正確的行高,tableView reloadData的時候,會重新計算contentSize,就有可能會引起contentOffset的變化。

iOS11下不想使用Self-Sizing的話,可以通過以下方式關閉:(前言中提到的問題也是通過這種方式解決的)

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

iOS11下,如果沒有設定estimateRowHeight的值,也沒有設定rowHeight的值,那contentSize計算初始值是 44 * cell的個數,如下圖:rowHeight和estimateRowHeight都是預設值UITableViewAutomaticDimension 而rowNum = 15;則初始contentSize = 44 * 15 = 660;

Table Views:separatorInset 擴充套件

iOS 7 引入separatorInset屬性,用以設定 cell 的分割線邊距,在 iOS 11 中對其進行了擴充套件。可以通過新增的UITableViewSeparatorInsetReference列舉型別的separatorInsetReference屬性來設定separatorInset屬性的參照值。

typedef NS_ENUM(NSInteger, UITableViewSeparatorInsetReference) {  
UITableViewSeparatorInsetFromCellEdges,   //預設值,表示separatorInset是從cell的邊緣的偏移量
UITableViewSeparatorInsetFromAutomaticInsets  //表示separatorInset屬性值是從一個insets的偏移量
}

下圖清晰的展示了這兩種參照值的區別:

Table Views 和 Safe Area

有以下幾點需要注意:

  • separatorInset 被自動地關聯到 safe area insets,因此,預設情況下,表檢視的整個內容避免了其根檢視控制器的安全區域的插入。
  • UITableviewCellUITableViewHeaderFooterView的 content view 在安全區域內;因此你應該始終在 content view 中使用add-subviews操作。
  • 所有的 headers 和 footers 都應該使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。

滑動操作(Swipe Actions)

在iOS8之後,蘋果官方增加了UITableVIew的右滑操作介面,即新增了一個代理方法(tableView: editActionsForRowAtIndexPath:)和一個類(UITableViewRowAction),代理方法返回的是一個數組,我們可以在這個代理方法中定義所需要的操作按鈕(刪除、置頂等),這些按鈕的類就是UITableViewRowAction。這個類只能定義按鈕的顯示文字、背景色、和按鈕事件。並且返回陣列的第一個元素在UITableViewCell的最右側顯示,最後一個元素在最左側顯示。從iOS 11開始有了一些改變,首先是可以給這些按鈕新增圖片了,然後是如果實現了以下兩個iOS 11新增的代理方法,將會取代(tableView: editActionsForRowAtIndexPath:)代理方法:

// Swipe actions
// These methods supersede -editActionsForRowAtIndexPath: if implemented
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath

這兩個代理方法返回的是UISwipeActionsConfiguration型別的物件,建立該物件及賦值可看下面的程式碼片段:

- ( UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    //刪除
    UIContextualAction *deleteRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
        [self.titleArr removeObjectAtIndex:indexPath.row];
        completionHandler (YES);
    }];
    deleteRowAction.image = [UIImage imageNamed:@"icon_del"];
    deleteRowAction.backgroundColor = [UIColor blueColor];

    UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[deleteRowAction]];
    return config;
}

建立UIContextualAction物件時,UIContextualActionStyle有兩種型別,如果是置頂、已讀等按鈕就使用UIContextualActionStyleNormal型別,delete操作按鈕可使用UIContextualActionStyleDestructive型別,當使用該型別時,如果是右滑操作,一直向右滑動某個cell,會直接執行刪除操作,不用再點選刪除按鈕,這也是一個好玩的更新。

typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
    UIContextualActionStyleNormal,
    UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)

滑動操作這裡還有一個需要注意的是,當cell高度較小時,會只顯示image,不顯示title,當cell高度夠大時,會同時顯示image和title。我寫demo測試的時候,因為每個cell的高度都較小,所以只顯示image,然後我增加cell的高度後,就可以同時顯示image和title了。見下圖對比:

總結

大概介紹了iOS 11的UI方面的一些更新,大部分內容都用程式碼測試過了,有些更新確實是很實用,可以適配下iOS 11,有的更新可能會給現有APP造成bug,所以學習下這些內容還是很有必要的。

更多精彩內容歡迎關注騰訊 Bugly的微信公眾賬號:

騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智慧合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的程式碼行,實時上報可以在釋出後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!

相關推薦

可能需要APPiOS11

前言 前幾天發現現在在做的APP在iOS11系統上動畫有異常,在其他系統的裝置上都是正常的,動畫的操作是觀察tableView的contentOffset變化後執行的,異常動畫發生在tableView reloadData之後,也就是說tableView reloadData之後,tableView的con

可能需要APP iOS 11

作者:sonialiu 導語:iOS 11 為整個生態系統的 UI 元素帶來了一種更加大膽、動態的新風格。 本文介紹iOS11中在UI方面做了哪些更新,有些更新可以為使用者提供更加完美的體驗,但也有的可能會給目前的APP帶來異常bug 前言

如何升級appiOS 7

開始工作 在這篇教程中,我們會將一個為iOS 6設計的app《Treasure Hunt》升級適配iOS 7。 Treasure Hunt是一個社交應用,允許使用者分享藏寶圖,使用者們可以沉浸於一起解決謎題以及尋找寶藏。這有點像地謎藏寶遊戲,它是一種有趣的戶外活

0c- iOS 11

適配 vpx amp scene tps mps acc cdb vpxd 參考路徑:https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=2653579210&idx=1&sn=d5ea8d46

iOS 11和iPhone X——導航欄、UITableView

每年的WWDC大會都激動和緊張好一段時間。激動的是期待蘋果的新產品帶來的那些黑科技,尤其今年的iPhone10週年紀念款iPhone X。緊張的當然是iOS、Swift和Xcode的升級,又要加班了(ㄒoㄒ)。在這裡跟大家分享一下適配iOS 11和iPhone X的過程中,遇到了一些坑。

iOS 11 & iPhone X

轉自:https://www.jianshu.com/p/94d3fdc0f20d   適配中的問題及解決辦法 1. 滾動條高度跳動、上下拉重新整理問題: self.tableView.estimatedRowHeight = 0; self.tableVie

iOS 11及iPhoneX iOS 11下tableView內容下移的問題

一.為什麼會發生內容下移 1.原因分析 在iOS 11中Apple幹掉了ViewController中的automaticallyAdjustsScrollViewInsets這個屬性,當tab

Android P的APP總結,讓快人一步

信息 最新 webview 包名 圖片 bmp illegal 名單 block 歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐幹貨哦~ 本文由QQ音樂技術團隊發表於雲+社區專欄 上篇:Android P 行為變更適配 Android P 這次有很多行為變更,其中不

1分鐘讓App 錘子OneStep

前言 釋出會是從一半開始看的,但是我沒錯過兩個東西 Big Bang 和 OneStep,這兩個東西從技術角度實現並不是什麼難事,只主要的就是龍哥的創意,讓人佩服不已,佩服不已,佩服不已。 本文重點 如何實現OneStep 遇到cont

AppiOS11

btn get eth rdl contain tag :after roo clas .markdown-body hr::after,.markdown-body::after { clear: both } .loopLine,.messageLine0 { } .m

一個全自動的屏幕方案(基於SW方案)!—— 解放和UI的雙手

寬度 double 屏幕尺寸 高度 組件化 center ply mar 結束 Calces系列相關文章:Calces自動實現Android組件化模塊構建 前言 屏幕適配一直是移動端開發熱議的問題,但是適配方案往往在實際開發的時候會和UI提供的設計稿沖突。本文主要

可能需要了解下Laravel集合

前言 集合通過 Illuminate\Support\Collection 進行例項,Laravel的核心大部分的引數傳遞都用到了集合,但這並不代表集合就是好的。Laravel作為快捷並優雅的開發框架,是有他一定的道理所在的,並非因他的路由、DB、監聽器等等。當你需要處理一組陣列時,你可能就需要它幫助你快

可能需要的一本前端小冊:Vue 專案構建與開發入門

最近抽時間寫了一本關於 Vue 的小冊:《Vue 專案構建與開發入門》,前前後後斷斷續續花了大概兩個月的時間。作為 Vue 的第一批使用者,同時也作為一名專欄作者,雖然我之前寫過好幾篇關於 Vue 的文章,但是該小冊是我第一本系統性介紹 Vue 專案構建與開發相關知識的書籍,也是我對於自己積累的 Vue 經驗

關於 MySQL LEFT JOIN 可能需要了解的三點

即使你認為自己已對 MySQL 的 LEFT JOIN 理解深刻,但我敢打賭,這篇文章肯定能讓你學會點東西! ON 子句與 WHERE 子句的不同 一種更好地理解帶有 WHERE ... IS NULL 子句的複雜匹配條件的簡單方法  Matching-Conditio

春節來臨-可能需要動態全屏閃爍的網頁賀卡【附帶演示地址和原始碼】

大神輕噴,我技術有限,純屬娛樂,不過有新的想法歡迎交流。 1.背景 之前看見別人做的動態文字視訊,還有炫酷的PPT,我就想自己嘗試做一下。 別人是用軟體做的,我想試試html。 然後就有了後面的故事了,開啟後播放速度會越來越快,最後會回到開始的速度,自己可以修改。 演

接手前端新專案?這裡有些注意點可能需要留意一下

前段時間加入公司內一個新開業務線的前端組,由於是新開的業務線,做的也是小程式這一塊,所以幾乎沒有任何歷史包袱,組內成員都是專案程式碼第一手產出者 我加入的時機較晚,沒有經歷過最開始的初創階段,不太清楚一開始的狀況,不過聽說是蠻折磨人的,需要踩坑無數,經常需要加班

RestTemplate使用過程中可能需要注意的點

RestTemplate在get請求設定引數時,url中後跟的引數一定不要是encode之後的param值,因為他會再次encode 一個url可以多次encode,每次結果不一 附上:原生jack

Java執行時環境初始化時出現錯誤,可能需要重灌Flash

安裝了Adobe Flash CS4的版本,由於是漢化綠化版,再加之之前一直用慣了flash8.0,很不習慣的是他的操作介面,特別可惡的是經常出現假死狀態。於是放棄這個版本,安裝了CS3的,畢竟8.0版的ActionScript 2.0已經逐漸被拋棄。3.0的已經在很大程度上

安裝 Laravel 出問題了?可能需要修改 composer.json 檔案

Composer最近對pre-update-command的工作方式做了修改,該項修改會導致Laravel安裝失敗。 Jordi Boggiano給出了修改的原因: 如果沒有composer.

通過ELK快速搭建一個可能需要的集中化日誌平臺

 在專案初期的時候,大家都是趕著上線,一般來說對日誌沒有過多的考慮,當然日誌量也不大,所以用log4net就夠了,隨著應用的越來越多,日誌散落在各個伺服器的logs資料夾下,確實有點不大方便,這個時候就想到了,在log4net中配置 mysql的資料來源,不過這裡面有一個坑,