ios11發(fā)布以來,很多新的特性為開發(fā)工作提供了方便,小編在此給大家介紹一下ios11的新特性以及在兼容適配等做的工作。
1. uiview變化
1.1. 更加方便的rtl邊距設(shè)置
在之前的系統(tǒng)中我們會使用layoutmargins來獲取和設(shè)置控件顯示內(nèi)容部分的邊緣與控件邊緣的距離。在ios 11中,新增directionallayoutmargins屬性來指定邊距。這兩個屬性的結(jié)構(gòu)定義如下:
1
2
3
4
5
6
7
|
typedef struct uiedgeinsets { cgfloat top, left, bottom, right; } uiedgeinsets; //// typedef struct nsdirectionaledgeinsets { cgfloat top, leading, bottom, trailing; } nsdirectionaledgeinsets |
從結(jié)構(gòu)上看主要是將uiedgeinsets結(jié)構(gòu)的left和right調(diào)整為nsdirectionaledgeinsets結(jié)構(gòu)的leading和trailing。這一調(diào)整主要是為了right to left(rtl)語言下可以進行自動適配,例如:要實現(xiàn)文本每行尾部邊距設(shè)置為30px,在以前做法則需要判斷語言來區(qū)分哪些是rtl語言,然后再做設(shè)置,如:
1
2
3
4
5
6
7
8
9
|
if ([uiview userinterfacelayoutdirectionforsemanticcontentattribute:self.view.semanticcontentattribute] == uiuserinterfacelayoutdirectionrighttoleft) { // right to left 語言下每行尾部在左邊 self.view.layoutmargins.left = 30; } else { self.view.layoutmargins.right = 30; } |
ios 11 后則可以一步到位,如:
1
|
self.view.directionallayoutmargins = nsdirectionaledgeinsetsmake(0, 0, 0, 30); |
注:測試時需要添加rtl本地化語言才能看到效果
1.2. 安全區(qū)域
在ios 11中新增了安全區(qū)域的概念,目的是告訴開發(fā)者在這個區(qū)域下繪制的內(nèi)容的顯示才是有效的,否則會存在被遮擋的情況(特別是iphonex那帥氣的劉海)。在uiview中新增safearealayoutguide和safeareainsets來獲取屏幕的安全區(qū)域(對于frame布局時是很有用的)。如圖所示:
safearea示意圖
舉個例子,在一個空白的uiviewcontroller中,分別在viewdidload和viewdidappear方法中輸出view.safeareainsets觀察邊距情況,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
- ( void )viewdidload { [super viewdidload]; nsstring *edgestr = nsstringfromuiedgeinsets(self.view.safeareainsets); nsstring *layoutfrmstr = nsstringfromcgrect(self.view.safearealayoutguide.layoutframe); nslog(@ "viewdidload safeareainsets = %@, layoutframe = %@" , edgestr, layoutfrmstr);= } - ( void )viewdidappear:( bool )animated { [super viewdidappear:animated]; nsstring *edgestr = nsstringfromuiedgeinsets(self.view.safeareainsets); nsstring *layoutfrmstr = nsstringfromcgrect(self.view.safearealayoutguide.layoutframe); nslog(@ "viewdidappear safeareainsets = %@, layoutframe = %@" , edgestr, layoutfrmstr); } |
可以看到其輸出為:
1
2
|
2017-09-19 14:45:50.246095+0800 sample[5608:1365070] viewdidload safeareainsets = {0, 0, 0, 0}, layoutframe = {{0, 0}, {375, 667}} 2017-09-19 14:45:50.257807+0800 sample[5608:1365070] viewdidappear safeareainsets = {20, 0, 0, 0}, layoutframe = {{0, 20}, {375, 603}} |
可見,在視圖顯示完成的時候view的頂部邊距變?yōu)榱?0px,而這20px正是狀態(tài)欄的高度。同樣原理,如果你的是一個
uinavigationcontroller那在顯示的時候view.safeareainsets就會變成{64, 0, 0, 0}。注意:在該vc下所有的uiview及其子類獲取到safeareainsets的值是相同的。
如果你想準(zhǔn)確地知道安全區(qū)域是什么時候被改變的,可以重寫uiview的safeareainsetsdidchange方法,在這個方法里面可以監(jiān)聽安全區(qū)域的邊距調(diào)整的事件(如果使用的是uiviewcontroller,其也提供相應(yīng)方法來實現(xiàn)監(jiān)聽,下一章節(jié)會講述該部分內(nèi)容),代碼如下:
1
2
3
4
|
- ( void )safeareainsetsdidchange { //寫入變更安全區(qū)域后的代碼... } |
如果你不想讓safeareainsets影響你的視圖布局,則可以將insetslayoutmarginsfromsafearea設(shè)置為no,所有的視圖布局將會忽略safeareainsets這個屬性了。要注意的是,insetslayoutmarginsfromsafearea僅用于autolayout,即使該屬性為no,視圖的safeareainsets還是一樣有值,而且安全區(qū)域變更方法safeareainsetsdidchange一樣被調(diào)用。
2. uiviewcontroller變化
2.1. 廢除api
2.1.1. automaticallyadjustsscrollviewinsets方法
ios 7中使用該方法來自動調(diào)整uiscrollview的contentinset。在ios 11之后將會使用uiscrollview的
contentinsetadjustmentbehavior屬性來代替該方法。
2.1.2. toplayoutguide和bottomlayoutguide屬性
ios 7中使用這兩個屬性來指導(dǎo)帶有導(dǎo)航欄(naviagtionbar)和頁簽欄(tabbar)的視圖排版。其作用如下圖所示
toplayoutguide & bottomlayoutguide
在ios 11之后將使用安全區(qū)域(safe area)來代替該部分功能的實現(xiàn)。
2.2. 排版
2.2.1. additionalsafeareainsets屬性
ios 11加入安全區(qū)域后,對于vc則可以通過該屬性來對該區(qū)域附加一個邊距信息。如:
1
|
self.additionalsafeareainsets = uiedgeinsetsmake(30, 0, 0, 30); |
注意:這里是附加邊距,意思就是在原有的safeareainsets值中增加對應(yīng)的邊距值。如果原來的是{10, 0, 0, 10}, 則最后得出的邊距是{40, 0, 0, 40}。
2.2.2. systemminimumlayoutmargins和viewrespectssystemminimumlayoutmargins屬性
該屬性表示了一個系統(tǒng)最小的邊距信息,所有的視圖排版都應(yīng)該遵循這個邊距信息的。除非將viewrespectssystemminimumlayoutmargins設(shè)置為no。
2.2.3. viewlayoutmarginsdidchange方法
根視圖的邊距變更時會觸發(fā)該方法的回調(diào)。可以通過該方法來處理當(dāng)邊距改變時子視圖的布局。
2.2.4. viewsafeareainsetsdidchange方法
當(dāng)視圖的安全區(qū)域發(fā)生變更時會觸發(fā)該方法的回調(diào)。可以通過該方法來處理安全區(qū)域變更時的子視圖布局。
3. uinavigationbar變化
ios 11中加入了大標(biāo)題模式,其顯示效果如下所示:
大標(biāo)題效果圖
實現(xiàn)該效果需要將導(dǎo)航欄的preferslargetitles設(shè)置為yes,如:
self.navigationcontroller.navigationbar.preferslargetitles = yes;
4. uinavigationitem變化
4.1 控制大標(biāo)題的顯示
如果你想控制每個視圖的大標(biāo)題是否顯示,這需要使用uinavigationitem的largetitledisplaymode屬性來控制大標(biāo)題的顯示。該屬性為枚舉類型,定義如下:
1
2
3
4
5
6
7
8
9
|
typedef ns_enum(nsinteger, uinavigationitemlargetitledisplaymode) { /// 自動模式,會繼承前一個navigationitem所設(shè)置的模式 uinavigationitemlargetitledisplaymodeautomatic, /// 當(dāng)前 navigationitem 總是啟用大標(biāo)題模式 uinavigationitemlargetitledisplaymodealways, /// 當(dāng)前 navigationitem 總是禁用大標(biāo)題模式 uinavigationitemlargetitledisplaymodenever, } |
根據(jù)上面的描述,可以在vc初始化init或者awakefromnib方法中設(shè)置顯示圖標(biāo)模式:
1
|
self.navigationitem.largetitledisplaymode = uinavigationitemlargetitledisplaymodealways; |
4.2 控制搜索控制器
ios 11 中新增了兩個屬性searchcontroller和hidessearchbarwhenscrolling。這兩個屬性主要用于簡化vc對uisearchcontroller的集成以及視覺優(yōu)化。其中searchcontroller屬性用于指定當(dāng)前vc的一個搜索控制器。而hidessearchbarwhenscrolling屬性則用于控制當(dāng)視圖滾動時是否隱藏搜索欄的ui,當(dāng)該值為yes時,搜索欄只有在內(nèi)容視圖(uiscrollview及其子類)頂部是才會顯示,在滾動過程中會隱藏起來;當(dāng)該值為no時,則不受滾動影響一直顯示在導(dǎo)航欄中。具體的代碼實現(xiàn)如下:
1
2
3
4
5
6
7
8
|
- ( void )awakefromnib { [super awakefromnib]; //設(shè)置searchcontroller到navigationitem self.searchcontroller = [[uisearchcontroller alloc] initwithsearchresultscontroller:self]; self.navigationitem.searchcontroller = self.searchcontroller; self.navigationitem.hidessearchbarwhenscrolling = yes; } |
搜索欄隱藏后效果
5. uiscrollview變化
之前的系統(tǒng)中,如果你的滾動視圖包含在一個導(dǎo)航控制器下,系統(tǒng)會自動地調(diào)整你的滾動視圖的contentinset。而ios 11新增adjustedcontentinset屬性取替之前contentinset的處理方式。這兩者之間的關(guān)系如下圖所示:
adjustedcontentinset & contentinset
通過一個例子來驗證這說法,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
- ( void )viewdidload { [super viewdidload]; nslog(@ "viewdidload" ); nslog(@ "self.tableview.contentinset = %@" , nsstringfromuiedgeinsets(self.tableview.contentinset)); nslog(@ "self.tableview.adjustedcontentinset = %@" , nsstringfromuiedgeinsets(self.tableview.adjustedcontentinset)); } - ( void )viewdidappear:( bool )animated { [super viewdidappear:animated]; nslog(@ "viewdidappear" ); nslog(@ "self.tableview.contentinset = %@" , nsstringfromuiedgeinsets(self.tableview.contentinset)); nslog(@ "self.tableview.adjustedcontentinset = %@" , nsstringfromuiedgeinsets(self.tableview.adjustedcontentinset)); } |
執(zhí)行后輸出下面信息:
1
2
3
4
5
6
|
2017-09-20 11:54:09.361348+0800 sample[1276:375286] viewdidload 2017-09-20 11:54:09.361432+0800 sample[1276:375286] self.tableview.contentinset = {0, 0, 0, 0} 2017-09-20 11:54:09.361462+0800 sample[1276:375286] self.tableview.adjustedcontentinset = {0, 0, 0, 0} 2017-09-20 11:54:09.420000+0800 sample[1276:375286] viewdidappear 2017-09-20 11:54:09.420378+0800 sample[1276:375286] self.tableview.contentinset = {0, 0, 0, 0} 2017-09-20 11:54:09.420554+0800 sample[1276:375286] self.tableview.adjustedcontentinset = {20, 0, 0, 0} |
可見,tableview的adjustedcontentinset自動改變了,但是contentinset的值是保持不變的。注:一定要是vc的根視圖為uiscrollview或者其子類才能夠得到adjustedcontentinset的值,否則獲取到的是空值。而且非根視圖的滾動視圖就會被安全區(qū)域所裁剪,看到的樣式如下圖所示:
樣式效果對比
通過使用contentinsetadjustmentbehavior屬性可以控制 adjustedcontentinset的變化。該屬性為枚舉類型,其定義如下:
1
2
3
4
5
6
|
typedef ns_enum(nsinteger, uiscrollviewcontentinsetadjustmentbehavior) { uiscrollviewcontentinsetadjustmentautomatic, uiscrollviewcontentinsetadjustmentscrollableaxes, uiscrollviewcontentinsetadjustmentnever, uiscrollviewcontentinsetadjustmentalways, } |
其中uiscrollviewcontentinsetadjustmentautomatic與uiscrollviewcontentinsetadjustmentscrollableaxes一樣,scrollview會自動計算和適應(yīng)頂部和底部的內(nèi)邊距并且在scrollview 不可滾動時,也會設(shè)置內(nèi)邊距;
uiscrollviewcontentinsetadjustmentnever表示不計算內(nèi)邊距;uiscrollviewcontentinsetadjustmentalways則根據(jù)視圖的安全區(qū)域來計算內(nèi)邊距。
如果需要感知adjustedcontentinset的變化,然后根據(jù)變化進行不同操作則可以通過重寫新增的adjustedcontentinsetdidchange方法或者實現(xiàn)uiscrollviewdelegate中的scrollviewdidchangeadjustedcontentinset方法來實現(xiàn)。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//重寫方法 - ( void )adjustedcontentinsetdidchange { [super adjustedcontentinsetdidchange]; //執(zhí)行操作... } //實現(xiàn)委托 - ( void )scrollviewdidchangeadjustedcontentinset:(uiscrollview *)scrollview { //執(zhí)行操作... } |
除了新增上述所說的邊距相關(guān)屬性外,還新增了contentlayoutguide和framelayoutguide屬性,用于描述內(nèi)容布局和整體布局信息。
6. ui主線程操作日志提醒
之前的系統(tǒng)中如果你不小心將ui放入非主線程操作時,debug日志是沒有任何信息反饋的,導(dǎo)致有時候在排錯時非常困難。在新的xcode 9中,如果你處于調(diào)試狀態(tài),將ui放入非主線程操作,如:
1
2
3
4
5
|
dispatch_async(dispatch_get_global_queue(0, 0), ^{ self.tv = [[uitableview alloc] initwithframe:self.view.bounds]; [self.view addsubview:self.tv]; nslog(@ "self.tv.adjustedcontentinset = %@" , nsstringfromuiedgeinsets(self.tv.adjustedcontentinset)); }); |
log中會出現(xiàn)下面提示:
1
2
3
4
5
6
7
8
9
10
11
12
|
================================================================= main thread checker: ui api called on a background thread : -[uiview bounds] pid: 16919, tid: 2972321, thread name: (none), queue name: com.apple.root. default -qos, qos: 21 backtrace: 4 sample 0x00000001004885dc __29-[viewcontroller viewdidload]_block_invoke + 112 5 libdispatch.dylib 0x000000010077149c _dispatch_call_block_and_release + 24 6 libdispatch.dylib 0x000000010077145c _dispatch_client_callout + 16 7 libdispatch.dylib 0x000000010077d56c _dispatch_queue_override_invoke + 980 8 libdispatch.dylib 0x0000000100782b54 _dispatch_root_queue_drain + 616 9 libdispatch.dylib 0x0000000100782880 _dispatch_worker_thread3 + 136 10 libsystem_pthread.dylib 0x000000018300b130 _pthread_wqthread + 1268 11 libsystem_pthread.dylib 0x000000018300ac30 start_wqthread + 4 |
從日志中了解到一個main thread checker的東西,根據(jù)蘋果官方文檔來看他是作用在appkit(osx中)、uikit還有一些相關(guān)api上的后臺線程,主要是用來監(jiān)控這些框架中的接口是否在主線程中進行調(diào)用,如果沒有則發(fā)出警告日志。因此,利用這個功能可以讓我們快速地定位那些地方存在問題。
以上就是本次ios11新特性與兼容適配介紹的全部內(nèi)容,如果大家還有任何問題可以在下方留言處留言討論。