UIScrollView and UITableView的新特性
ScrollView
如果有一些文本位于UI滾動(dòng)視圖的內(nèi)部卿城,并包含在導(dǎo)航控制器中,現(xiàn)在一般navigationContollers會(huì)傳入一個(gè)contentInset
給其最頂層的viewController的scrollView,在iOS11中進(jìn)行了一個(gè)很大的改變巩螃,不再通過scrollView的contentInset
屬性了,而是新增了一個(gè)屬性:adjustedContentInset
坦袍,通過下面兩種圖的對(duì)比,能夠表示adjustContentInset
表示的區(qū)域:
新增的contentInsetAdjustmentBehavior
屬性用來配置adjustedContentInset
的行為等太,該結(jié)構(gòu)體有以下幾種類型:
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;
UIScrollViewContentInsetAdjustmentBehavior 是一個(gè)枚舉類型,值有以下幾種:
- automatic 和scrollableAxes一樣,scrollView會(huì)自動(dòng)計(jì)算和適應(yīng)頂部和底部的內(nèi)邊距并且在scrollView 不可滾動(dòng)時(shí),也會(huì)設(shè)置內(nèi)邊距.
- scrollableAxes 自動(dòng)計(jì)算內(nèi)邊距.
- never不計(jì)算內(nèi)邊距
- always 根據(jù)safeAreaInsets 計(jì)算內(nèi)邊距
TableView
1.UITableview UICollectionView MJRefresh下拉刷新錯(cuò)亂或是莫名有20空隙的問題
if (@available(iOS 11.0, *)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
_tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);//iPhoneX這里是88
_tableView.scrollIndicatorInsets = _tableView.contentInset;
}
如果改完后運(yùn)行無效果 可以嘗試Clean一下工程再運(yùn)行, 很多小伙伴都遇到這樣的問題了, 你不妨也試以試.
2.在iOS 11中默認(rèn)啟用Self-Sizing 未使用AutoLayout的TableView中的高度會(huì)出現(xiàn)問題.
Self-Sizing
在iOS11下是默認(rèn)開啟的,Headers, footers, and cells都默認(rèn)開啟Self-Sizing
缩抡,所有estimated
高度默認(rèn)值從iOS11之前的 0
改變?yōu)?code>UITableViewAutomaticDimension.
如果目前項(xiàng)目中沒有使用estimateRowHeight屬性奠宜,在iOS11的環(huán)境下就要注意了,因?yàn)殚_啟Self-Sizing
之后瞻想,tableView是使用estimateRowHeight
屬性的压真,這樣就會(huì)造成contentSize
和contentOffset
值的變化,如果是有動(dòng)畫是觀察這兩個(gè)屬性的變化進(jìn)行的蘑险,就會(huì)造成動(dòng)畫的異常滴肿,因?yàn)樵诠浪阈懈邫C(jī)制下,contentSize
的值是一點(diǎn)點(diǎn)地變化更新的佃迄,所有cell顯示完后才是最終的contentSize
值泼差。因?yàn)椴粫?huì)緩存正確的行高,tableView reloadData
的時(shí)候呵俏,會(huì)重新計(jì)算contentSize
堆缘,就有可能會(huì)引起contentOffset
的變化。iOS11下不想使用Self-Sizing
的話普碎,可以通過以下方式關(guān)閉:
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
3.TableView的separatorInset擴(kuò)展
iOS 7 引入separatorInset
屬性吼肥,用以設(shè)置 cell 的分割線邊距,在 iOS 11 中對(duì)其進(jìn)行了擴(kuò)展随常∏甭伲可以通過新增的UITableViewSeparatorInsetReference
枚舉類型的separatorInsetReference
屬性來設(shè)置separatorInset
屬性的參照值.
通過下面的參考圖可以看出他們的區(qū)別:
4. TableView和SafeArea(安全區(qū))
有以下幾點(diǎn)需要注意:
-
separatorInset
被自動(dòng)地關(guān)聯(lián)到 safe area insets,因此绪氛,默認(rèn)情況下唆鸡,表視圖的整個(gè)內(nèi)容避免了其根視圖控制器的安全區(qū)域的插入。 -
UITableviewCell
和UITableViewHeaderFooterView
的contentview
在安全區(qū)域內(nèi)枣察;因此你應(yīng)該始終在contentview
中使用add-subviews
操作争占。 - 所有的 headers 和 footers 都應(yīng)該使用
UITableViewHeaderFooterView
,包括 table headers 和 footers序目、section headers 和 footers臂痕。
5. TableView的滑動(dòng)操作
在iOS8之后,蘋果官方增加了UITableVIew的右滑操作接口猿涨,即新增了一個(gè)代理方法tableView: editActionsForRowAtIndexPath:
和一個(gè)類UITableViewRowAction
握童,代理方法返回的是一個(gè)數(shù)組,我們可以在這個(gè)代理方法中定義所需要的操作按鈕(刪除叛赚、置頂?shù)?澡绩,這些按鈕的類就是UITableViewRowAction
稽揭。這個(gè)類只能定義按鈕的顯示文字、背景色肥卡、和按鈕事件溪掀。并且返回?cái)?shù)組的第一個(gè)元素在UITableViewCell
的最右側(cè)顯示,最后一個(gè)元素在最左側(cè)顯示步鉴。從iOS 11開始有了一些改變揪胃,首先是可以給這些按鈕添加圖片了,然后是如果實(shí)現(xiàn)了以下兩個(gè)iOS 11新增的代理方法氛琢,將會(huì)取代tableView: editActionsForRowAtIndexPath:
代理方法:
這兩個(gè)代理方法返回的是UISwipeActionsConfiguration
類型的對(duì)象喊递,創(chuàng)建該對(duì)象及賦值可看下面的代碼片段:
- ( 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;
}
typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
UIContextualActionStyleNormal,
UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)
創(chuàng)建UIContextualAction
對(duì)象時(shí),UIContextualActionStyle
有兩種類型艺沼,如果是置頂册舞、已讀等按鈕就使用UIContextualActionStyleNormal
類型,delete操作按鈕可使用UIContextualActionStyleDestructive
類型障般,當(dāng)使用該類型時(shí)调鲸,如果是右滑操作,一直向右滑動(dòng)某個(gè)cell挽荡,會(huì)直接執(zhí)行刪除操作藐石,不用再點(diǎn)擊刪除按鈕,這也是一個(gè)好玩的更新.
typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
UIContextualActionStyleNormal,
UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)
滑動(dòng)操作這里還有一個(gè)需要注意的是定拟,當(dāng)cell高度較小時(shí)于微,會(huì)只顯示image,不顯示title青自,當(dāng)cell高度夠大時(shí)株依,會(huì)同時(shí)顯示image和title。我寫demo測試的時(shí)候延窜,因?yàn)槊總€(gè)cell的高度都較小恋腕,所以只顯示image,然后我增加cell的高度后逆瑞,就可以同時(shí)顯示image和title了荠藤。見下圖對(duì)比:
iOS11中 UIKit’s Bars 上的變化
WWDC通過iOS新增的文件管理App:Files開始介紹,在Files這個(gè)APP中能夠看到iOS11中UIKit’s Bars的一些新特性:在瀏覽功能上的大標(biāo)題視圖(向上滑動(dòng)后標(biāo)題會(huì)回到原來的UI效果)获高、橫屏狀態(tài)下tab上的文字和icon會(huì)變?yōu)樽笥遗帕校?/p>
在iPhone上哈肖,tab上的圖標(biāo)較小,tab bar較小念秧,這樣垂直空間可多放置內(nèi)容淤井。如果有人看不清楚tab bar上的圖標(biāo)或文字,可以通過長按tab bar上的任意item,會(huì)將該item顯示在HUD上庄吼,這樣可以清楚的看清icon和text缎除。對(duì)tool bar 和 navigation bar同理严就,長按item也會(huì)放大顯示.
- UIBarItem
UIBarItem是UI tab bar item和UI bar button item的父類总寻,要想實(shí)現(xiàn)上面介紹的效果,只需要為UIBarItem 設(shè)置landscapeImagePhone
屬性梢为,在storyboard中也支持這個(gè)設(shè)置渐行,對(duì)于HUD的image需要設(shè)置另一個(gè)iOS11新增的屬性:largeContentSizeImage
,關(guān)于這部分更詳細(xì)的討論铸董,可以參考 WWDC2017 Session 215:What's New in Accessibility
- 控制大標(biāo)題的顯示
在UINavigationbar
中新增了一個(gè)BOOL屬性prefersLargeTitles
,將該屬性設(shè)置為ture
祟印,navigationbar就會(huì)在整個(gè)APP中顯示大標(biāo)題,如果想要在控制不同頁面大標(biāo)題的顯示粟害,可以通過設(shè)置當(dāng)前頁面的navigationItem
的largeTitleDisplayMode
屬性.
navigationItem.largeTitleDisplayMode
typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {
/// 自動(dòng)模式依賴上一個(gè) item 的特性
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 針對(duì)當(dāng)前 item 總是啟用大標(biāo)題特性
UINavigationItemLargeTitleDisplayModeAlways,
/// Never
UINavigationItemLargeTitleDisplayModeNever,
}
在 Navigation 集成 UISearchController
把你的UISearchController
賦值給navigationItem
蕴忆,就可以實(shí)現(xiàn)將UISearchController
集成到 Navigation.
navigationItem.searchController //iOS 11 新增屬性
navigationItem.hidesSearchBarWhenScrolling //決定滑動(dòng)的時(shí)候是否隱藏搜索框;iOS 11 新增屬性
UINavigationController和滾動(dòng)交互
滾動(dòng)的時(shí)候悲幅,以下交互操作都是由UINavigationController負(fù)責(zé)調(diào)動(dòng)的:
UIsearchController搜索框效果更新
大標(biāo)題效果的控制
Rubber banding效果 //當(dāng)你開始往下拉套鹅,大標(biāo)題會(huì)變大來回應(yīng)那個(gè)滾輪
所以,如果你使用navigation bar汰具,組裝一些整體push和pop體驗(yàn)卓鹿,你不會(huì)得到searchController的集成、大標(biāo)題的控制更新和Rubber banding效果留荔,因?yàn)檫@些都是由UINavigationController
控制的吟孙。
Margins 和 Insets
基于約束的Auto Layout, 使我們搭建能夠動(dòng)態(tài)響應(yīng)內(nèi)部和外部變化的用戶界面. Auto Layout為每一個(gè)view
都定義了margin
. margin
指的是控件顯示內(nèi)容部分的邊緣和控件邊緣的距離.
可以用layoutMargins
或者layoutMarginsGuide
屬性獲得view
的margin
, margin
是視圖內(nèi)部的一部分. layoutMargins
允許獲取或者設(shè)置UIEdgeInsets
結(jié)構(gòu)的margin
. layoutMarginsGuide
則獲取到只讀的UILayoutGuide
對(duì)象.
在iOS11新增了一個(gè)屬性:directional layout margins
,該屬性是NSDirectionalEdgeInsets
結(jié)構(gòu)體類型的屬性:
typedef struct NSDirectionalEdgeInsets {
CGFloat top, leading, bottom, trailing;
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));
layoutMargins
是UIEdgeInsets
結(jié)構(gòu)體類型的屬性:
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right;
} UIEdgeInsets;
從上面兩種結(jié)構(gòu)體的對(duì)比可以看出, 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.
例子: 當(dāng)你設(shè)置了trailing = 30;
當(dāng)在一個(gè)right to left 語言下trailing
的值會(huì)被設(shè)置在view
的左邊, 可以通過layoutMargin
的left
屬性讀出該值. 如下圖所示:
還有其他一些更新. 自從引入layout margins
, 當(dāng)將一個(gè)view
添加到viewController
時(shí), viewController
會(huì)修改view
的layoutMargins
為UIKit定義的一個(gè)值, 這些調(diào)整對(duì)外是封閉的. 從iOS11開始, 這些不再是一個(gè)固定的值, 它們實(shí)際是最小值, 你可以改變view
的layoutMargins
為任意一個(gè)更大的值. 而且, viewController
新增了一個(gè)屬性: viewRespectsSystemMinimumLayoutMargins
, 如果你設(shè)置該屬性為false
, 你就可以改變你的layoutMargins
為任意你想設(shè)置的值, 包括0
, 如下圖所示: