一、NavigationBar
UIBarItem
UIBarItem
在iOS11在中新增landscapeImagePhone
屬性梢为,用來在小圖看不清楚的情況下,長按顯示放大的圖羞酗。在storyboard中也支持這個設(shè)置摧找,對于HUD的image需要設(shè)置另一個iOS11新增的屬性:largeContentSizeImage
,關(guān)于這部分更詳細(xì)的討論喇完,可以參考 WWDC2017 Session 215:What's New in Accessibility
返回按鈕的適配
iOS11之前移除返回按鈕文字的代碼:
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
iOS 11 中該方法依舊可以把文字移走伦泥,但是會往下偏一點(diǎn),很不美觀锦溪,并且編輯器會報(bào)出如下類似的警告
我的解決方法是將push
方法包一層不脯,自定義每個vc的navigationItem.backBarButtonItem
,這種方式的時候刻诊,在childViewControllers
使用push方法的時候會失效防楷,需要使用當(dāng)前viewController
進(jìn)行push操作
func dzy_push(_ vc:UIViewController, hide:Bool = false, animated:Bool = true) {
if hide {
vc.hidesBottomBarWhenPushed = hide
}
let btn = UIButton(type: .custom)
btn.setImage(UIImage(named: "navi_back"), for: .normal)
btn.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)
btn.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
//隱藏返回按鈕中的文字
navigationItem.backBarButtonItem = UIBarButtonItem(customView: btn)
navigationController?.pushViewController(vc, animated: animated)
}
可選擇思路:
- 自定義
navigationController
,重寫push方法则涯,在push方法中做類似的事情 - 在
viewController
中做文章复局,重寫返回按鈕
LargeTitle
在iOS11導(dǎo)航欄多了一個LargeTitleView
冲簿,默認(rèn)是不開啟的,專門顯示大字標(biāo)題用的亿昏,整個導(dǎo)航欄的高度達(dá)到了96p
峦剔,這不包括狀態(tài)欄的高度,也就是說角钩,整個app頂部高度達(dá)到了116p
吝沫,其中statusbar=20,title=44递礼,largetitle=52
野舶,不過默認(rèn)是64p
;當(dāng)然宰衙,iPhoneX的高度會更高點(diǎn)平道,如果不顯示大字標(biāo)題,頂部的高度也達(dá)到了88
供炼,statusbar=44一屋,title=44
,如果顯示大字標(biāo)題袋哼,則高度變成了140
冀墨,statusbar=44,title=44涛贯,largetitle=52
篙螟,也就是說众旗,iPhoneX的劉海高度為24p
,大字標(biāo)題如下圖:
具體設(shè)置:
在UI navigation bar中新增了一個BOOL屬性prefersLargeTitles
,將該屬性設(shè)置為ture
,navigation bar就會在整個APP中顯示大標(biāo)題势似,如果想要在控制不同頁面大標(biāo)題的顯示,可以通過設(shè)置當(dāng)前頁面的navigationItem
的largeTitleDisplayMode
屬性国葬;
navigationItem.largeTitleDisplayMode
typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {
/// 自動模式依賴上一個 item 的特性
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 針對當(dāng)前 item 總是啟用大標(biāo)題特性
UINavigationItemLargeTitleDisplayModeAlways,
/// Never
UINavigationItemLargeTitleDisplayModeNever,
}
Navigation 集成 UISearchController
把你的UISearchController
賦值給navigationItem
安吁,就可以實(shí)現(xiàn)將UISearchController
集成到NavigationBar
。
navigationItem.searchController //iOS 11 新增屬性
navigationItem.hidesSearchBarWhenScrolling //決定滑動的時候是否隱藏搜索框睛琳;iOS 11 新增屬性
導(dǎo)航欄的邊距變化
-
如果只是設(shè)置了
titleView
盒蟆,沒有設(shè)置barbutton
,把titleview
的寬度設(shè)置為屏幕寬度师骗,則titleview
距離屏幕的邊距- iOS11之前历等,在iPhone6p上是
20p
,在iPhone6p之前是16p
- iOS11之后辟癌,在iPhone6p上是
12p
寒屯,在iPhone6p之前是8p
- iOS11之前历等,在iPhone6p上是
-
如果只是設(shè)置了
barbutton
,沒有設(shè)置titleview
- 在iOS11里愿待,
barButton
距離屏幕的邊距是20p
和16p
- 在iOS11之前浩螺,
barButton
距離屏幕的邊距也是20p
和16p
- 在iOS11里愿待,
-
如果同時設(shè)置了
titleView
和barButton
- 則在iOS11之前靴患,titleview和barbutton之間的間距是6p
- 在iOS11上titleview和barbutton之間無間距,如下圖:
UINavigationController和滾動交互
滾動的時候要出,以下交互操作都是由UINavigationController
負(fù)責(zé)調(diào)動的:
-
UIsearchController
搜索框效果更新 - 大標(biāo)題效果的控制
-
Rubber banding效果
//當(dāng)你開始往下拉鸳君,大標(biāo)題會變大來回應(yīng)那個滾輪
所以,如果你使用navigation bar患蹂,組裝一些整個push和pop體驗(yàn)或颊,你不會得到searchController的集成、大標(biāo)題的控制更新和Rubber banding效果传于,因?yàn)檫@些都是由UINavigationController控制的囱挑。
UIToolbar and UINavigationBar— Layout
在 iOS 11 中,當(dāng)蘋果進(jìn)行所有這些新特性時沼溜,也進(jìn)行了其他的優(yōu)化平挑,針對 UIToolbar 和 UINavigaBar 做了新的自動布局?jǐn)U展支持,自定義的bar button items系草、自定義的title都可以通過layout來表示尺寸通熄。
需要注意的是,你的constraints需要在view內(nèi)部設(shè)置找都,所以如果你有一個自定義的標(biāo)題視圖唇辨,你需要確保任何約束只依賴于標(biāo)題視圖及其任何子視圖。當(dāng)你使用自動布局能耻,系統(tǒng)假設(shè)你知道你在做什么赏枚。
Avoiding Zero-Sized Custom Views
自定義視圖的size為0是因?yàn)槟阌幸恍┠:募s束布局。要避免視圖尺寸為0晓猛,可以從以下方面做:
- UINavigationBar 和 UIToolbar 提供位置
- 開發(fā)者則必須提供視圖的size饿幅,有三種方式:
- 對寬度和高度的約束;
- 實(shí)現(xiàn) intrinsicContentSize鞍帝;
- 通過約束關(guān)聯(lián)你的子視圖诫睬;
二、UITabBar
iPhoneX不止多了劉海帕涌,底部還有一個半角的矩形,使得tabbar多出來了34p
的高度续徽,不過不管navigationBar
還是tabbar
一般系統(tǒng)都會自動適配safeArea蚓曼。
三、管理margins 和 insets
layout margins
基于約束的Auto Layout钦扭,使我們搭建能夠動態(tài)響應(yīng)內(nèi)部和外部變化的用戶界面纫版。Auto Layout為每一個view
都定義了margin
。margin
指的是控件顯示內(nèi)容部分的邊緣和控件邊緣的距離客情。
可以用layoutMargins
或者layoutMarginsGuide
屬性獲得view
的margin
,margin
是視圖內(nèi)部的一部分其弊。layoutMargins
允許獲取或者設(shè)置UIEdgeInsets
結(jié)構(gòu)的margin
癞己。layoutMarginsGuide
則獲取到只讀的UILayoutGuide
對象。
在iOS11新增了一個屬性: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)體的對比可以看出痹雅,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)在一個right to left
語言下trailing
的值會被設(shè)置在view
的左邊绩社,可以通過layoutMargin
的left
屬性讀出該值。如下圖所示:
還有其他一些更新赂苗。自從引入
layout margins
愉耙,當(dāng)將一個view
添加到viewController
時,viewController
會修復(fù)view
的layoutMargins
為UIKit
定義的一個值拌滋,這些調(diào)整對外是封閉的朴沿。從iOS11開始,這些不再是一個固定的值败砂,它們實(shí)際是最小值赌渣,你可以改變view
的layoutMargins
為任意一個更大的值。而且吠卷,viewController
新增了一個屬性:viewRespectsSystemMinimumLayoutMargins
锡垄,如果你設(shè)置該屬性為"false"
,你就可以改變你的layoutMargins
為任意你想設(shè)置的值祭隔,包括0货岭,如下圖所示:
安全區(qū)域(Safe Area)
iOS 7 開始,在 UIViewController中引入的 topLayoutGuide
和 bottomLayoutGuide
在 iOS 11 中被廢棄了疾渴!取而代之的就是safeArea的概念千贯,safeArea是描述你的視圖部分不被任何內(nèi)容遮擋的方法。 它提供兩種方式:safeAreaInsets
或safeAreaLayoutGuide
來提供給你safeArea的參照值搞坝,即 insets 或者 layout guide搔谴。 safeArea區(qū)域如圖所示:
如果有一個自定義的viewController,你可能要添加你自己的bars桩撮,增加safeAreaInsets
的值敦第,可以通過一個新的屬性:addtionalSafeAreaInsets
來改變safeAreaInsets
的值,當(dāng)你的viewController改變了它的safeAreaInsets
值時店量,有兩種方式獲取到回調(diào):
UIView.safeAreaInsetsDidChange()
UIViewController.viewSafeAreaInsetsDidChange()
四芜果、UIScrollView and UITableView的新特性
UIScrollView (棄用automaticallyAdjustsScrollViewInsets)
如果有一些文本位于UI滾動視圖的內(nèi)部,并包含在導(dǎo)航控制器中融师,一般navigationContollers會傳入一個contentInset
給其最頂層的viewController的scrollView右钾,在iOS11中進(jìn)行了一個很大的改變,不再通過scrollView的contentInset
屬性了,而是新增了一個屬性:adjustedContentInset
舀射,通過下面兩種圖的對比窘茁,能夠表示adjustContentInset
表示的區(qū)域:
取消掉原有的
automaticallyAdjustsScrollViewInsets
屬性,新增的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;
針對automaticallyAdjustsScrollViewInsets
屬性被棄用,比較簡單的適配方法
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
self.automaticallyAdjustsScrollViewInsets = NO;
}
Table Views :在iOS 11中默認(rèn)啟用Self-Sizing
這個應(yīng)該是UITableView最大的改變浩淘。我們知道在iOS8引入Self-Sizing 之后捌朴,我們可以通過實(shí)現(xiàn)estimatedRowHeight
相關(guān)的屬性來展示動態(tài)的內(nèi)容,實(shí)現(xiàn)了estimatedRowHeight
屬性后张抄,得到的初始contenSize
是個估算值砂蔽,是通過estimatedRowHeight
x cell
的個數(shù)得到的,并不是最終的contenSize
署惯,tableView
不會一次性計(jì)算所有的cell
的高度了左驾,只會計(jì)算當(dāng)前屏幕能夠顯示的cell
個數(shù)再加上幾個,滑動時极谊,tableView
不停地得到新的cell
诡右,更新自己的contenSize
,在滑到最后的時候轻猖,會得到正確的contenSize
帆吻。創(chuàng)建tableView
到顯示出來的過程中,contentSize
的計(jì)算過程如下圖:
Self-Sizing在iOS11下是默認(rèn)開啟的咙边,Headers, footers, and cells都默認(rèn)開啟Self-Sizing猜煮,所有
estimated
高度默認(rèn)值從iOS11之前的 0 改變?yōu)?code>UITableViewAutomaticDimension由于Self-Sizing的緣故,
contentSize
和contentOffset
的值在滾動的過程中會一直產(chǎn)生變化败许,所以如果目前的項(xiàng)目你還沒有使用到Self-Sizing王带,contentSize
和contentOffset
有關(guān)的動畫可能會有問題。
iOS 11中如果不實(shí)現(xiàn)-tableView: viewForFooterInSection:
和-tableView: viewForHeaderInSection:
市殷,那么-tableView: heightForHeaderInSection:
和- tableView: heightForFooterInSection:
不會被調(diào)用愕撰。
iOS11下不想使用Self-Sizing的話,可以通過以下方式關(guān)閉:
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
Table Views:separatorInset 擴(kuò)展
iOS 7 引入separatorInset
屬性醋寝,用以設(shè)置 cell
的分割線邊距搞挣,在 iOS 11 中對其進(jìn)行了擴(kuò)展∫粜撸可以通過新增的UITableViewSeparatorInsetReference
枚舉類型的separatorInsetReference
屬性來設(shè)置separatorInset
屬性的參照值柿究。
typedef NS_ENUM(NSInteger, UITableViewSeparatorInsetReference) {
UITableViewSeparatorInsetFromCellEdges, //默認(rèn)值,表示separatorInset是從cell的邊緣的偏移量
UITableViewSeparatorInsetFromAutomaticInsets //表示separatorInset屬性值是從一個insets的偏移量
}
下圖清晰的展示了這兩種參照值的區(qū)別:
Table Views 和 Safe Area
有以下幾點(diǎn)需要注意:
-
separatorInset
被自動地關(guān)聯(lián)到 safe area insets黄选,因此,默認(rèn)情況下,表視圖的整個內(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
。
滑動操作(Swipe Actions)
在iOS8之后制圈,蘋果官方增加了UITableVIew
的右滑操作接口们童,即新增了一個代理方法(tableView: editActionsForRowAtIndexPath:)
和一個類(UITableViewRowAction)
,代理方法返回的是一個數(shù)組鲸鹦,我們可以在這個代理方法中定義所需要的操作按鈕(刪除慧库、置頂?shù)?,這些按鈕的類就是UITableViewRowAction
馋嗜。這個類只能定義按鈕的顯示文字齐板、背景色、和按鈕事件葛菇。并且返回?cái)?shù)組的第一個元素在UITableViewCell
的最右側(cè)顯示甘磨,最后一個元素在最左側(cè)顯示。從iOS 11開始有了一些改變眯停,首先是可以給這些按鈕添加圖片了济舆,然后是如果實(shí)現(xiàn)了以下兩個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
類型的對象莺债,創(chuàng)建該對象及賦值可看下面的代碼片段:
- ( 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;
}
創(chuàng)建UIContextualAction
對象時滋觉,UIContextualActionStyle
有兩種類型,如果是置頂九府、已讀等按鈕就使用UIContextualActionStyleNormal
類型椎瘟,delete
操作按鈕可使用UIContextualActionStyleDestructive
類型,當(dāng)使用該類型時侄旬,如果是右滑操作肺蔚,一直向右滑動某個cell,會直接執(zhí)行刪除操作儡羔,不用再點(diǎn)擊刪除按鈕宣羊,這也是一個好玩的更新。
typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
UIContextualActionStyleNormal,
UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)
滑動操作這里還有一個需要注意的是汰蜘,當(dāng)cell
高度較小時仇冯,會只顯示image
,不顯示title
族操,當(dāng)cell
高度夠大時苛坚,會同時顯示image
和title
比被。我寫demo測試的時候,因?yàn)槊總€cell
的高度都較小泼舱,所以只顯示image
等缀,然后我增加cell
的高度后,就可以同時顯示image
和title
了娇昙。見下圖對比:
參考鏈接
http://www.reibang.com/p/370d82ba3939
http://www.reibang.com/p/352f101d6df1