寫在之前的大篇廢話
研究adaptivePresentationStyleForPresentationController和iPhone上popover一個(gè)popoverPresentationController的關(guān)系無(wú)果后,一怒之下翻譯該文檔,
希望不要浪費(fèi)太多時(shí)間 -- 9月28號(hào)
結(jié)果學(xué)到了很多東西,雖然還沒(méi)解決問(wèn)題 -- 9月29號(hào)
問(wèn)題解決了,原來(lái)都在文檔里,只是我沒(méi)讀懂讀透 --9月30號(hào)
廢話:-----通過(guò)這次翻譯的經(jīng)歷,感覺(jué)到沉下心來(lái)做一件事的重要性,28號(hào)的時(shí)候無(wú)意間研究到iphone的popover,發(fā)現(xiàn)自己怎么也不能在iphone上做出一個(gè)popover出來(lái),老是全屏,搗鼓了大半天,把度娘和谷歌翻了個(gè)遍,覺(jué)得確實(shí)是照著人家的帖子做的,為啥不出呢?
翻到ios官網(wǎng)的這篇文章,總覺(jué)得讀了沒(méi)有收獲,(我英語(yǔ)都認(rèn)識(shí),可是讀完也不知道它在說(shuō)什么)但是又覺(jué)得它確實(shí)提到了我所說(shuō)的問(wèn)題,反正也挺無(wú)奈的,就決定逐字逐句的讀一讀這文章,看看是不是自己有什么沒(méi)懂的地方.
每句讀了也不了解的話,我都姑且照字翻譯了下,然后會(huì)去查閱相關(guān)的文檔,官網(wǎng)優(yōu)先,百度也問(wèn),也翻這本書:Programming.iOS.9.在逐步學(xué)習(xí)的過(guò)程中,發(fā)現(xiàn)了很多平時(shí)我掌握的似是而非的東西,如size classes,如vc間的present dismiss等等,雖然做ios已經(jīng)兩年了,這些東西每天都在弄,但是它很多細(xì)微的東西還沒(méi)有深入的了解.
慢慢的融會(huì)貫通幾個(gè)知識(shí)點(diǎn),理解了很多東西,雖然這些東西平時(shí)沒(méi)人會(huì)問(wèn)你為什么,找工作別人也不會(huì)問(wèn)這么細(xì),浪費(fèi)時(shí)間想這些似乎沒(méi)有多大的意思,沒(méi)有弄些時(shí)尚流行的技術(shù)來(lái)得可以吹niub.但是還是感觸很深,很多簡(jiǎn)單的功能,會(huì)做.但是為什么這么做,深層的意義在哪里,內(nèi)涵思想是什么,研究了這些細(xì)節(jié)后,才有所體會(huì).之前還是很心浮氣躁,比如9月28號(hào)的時(shí)候,看了五六個(gè)帖子,覺(jué)得自己已經(jīng)會(huì)了popover的步驟,就是不出效果,快要放棄了.這兩天看了一些文檔,才領(lǐng)會(huì)了為啥這么做,為啥要寫代理,還可以舉一反三等等.讀的多了,發(fā)現(xiàn)某些大神的所謂深入帖子,也只是把官網(wǎng)的幾篇文檔綜合了一下,加上自己的總結(jié).可見(jiàn)編程其實(shí)和做學(xué)問(wèn)差不多,要放下心中的浮躁,慢節(jié)奏的讀一些似乎沒(méi)用的東西.
問(wèn)題的解決經(jīng)過(guò):
之前照著這個(gè)帖子做的,http://www.itdecent.cn/p/e44542c38fc9 始終不能出現(xiàn)popover,總是全屏,我認(rèn)為肯定是adaptivePresentationStyleForPresentationController這個(gè)函數(shù)出的問(wèn)題,調(diào)試發(fā)現(xiàn)從來(lái)沒(méi)進(jìn)入過(guò),然后差點(diǎn)放棄了.
我也沒(méi)問(wèn)過(guò)自己為什么一定要實(shí)現(xiàn)這個(gè)方法,原因是啥.就知道很多帖子都說(shuō)了這個(gè), 直到翻譯完了文檔,才知道:
其實(shí)在swift3中不是這個(gè)函數(shù)(OC中是上面那個(gè)函數(shù)),而是:
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle{
return .none
}
1) 它是UIViewController的屬性presentationController的代理UIPresentationControllerDelegate的函數(shù).
2) 同理,UIViewController的屬性popoverPresentationController的代理UIPopoverPresentationControllerDelegate,由于繼承了UIPresentationControllerDelegate的原因,所以也要實(shí)現(xiàn)這個(gè)方法.
關(guān)系如圖1-1-1:

實(shí)現(xiàn)了它頓時(shí)就好了.所以坑就在這里
這是我的代碼:
@IBAction func clickBtn(_ sender: AnyObject) {
let pop = PopViewController()
//這個(gè)屬性必須實(shí)現(xiàn),否則popoverPresentationController為nil
pop.modalPresentationStyle = .popover
//設(shè)置代理,用 adaptivePresentationStyle(for controller) 指定不要全屏顯示
pop.popoverPresentationController?.delegate = self
//popover 的大小
pop.preferredContentSize = CGSize(width:100, height:100)
//popover基于哪個(gè)view出來(lái)
pop.popoverPresentationController?.sourceView = btn
pop.popoverPresentationController?.sourceRect? = CGRect.zero
//? ? ? ? pop.popoverPresentationController?.sourceRect? = CGRect(origin:CGPoint(x:100,y:100) ,size:CGSize(width:0,height:0))
//popover的箭頭方向
pop.popoverPresentationController?.permittedArrowDirections = .down
self.present(pop, animated: true, completion: nil)
}
xib或者storyboard里的pop 不需要做任何改動(dòng).比如這個(gè)帖子寫的https://my.oschina.net/sayonala/blog/533888 ?都不用
效果:

翻譯之前的儲(chǔ)備
1. size classes
1) size classes 是ios8引進(jìn)的的一種對(duì)布局的自適應(yīng)的解決方案.可以從以下兩個(gè)方面理解:
對(duì)不同設(shè)備的屏幕尺寸,包括橫屏和豎屏,我們可以設(shè)定不同的view布局,系統(tǒng)會(huì)檢測(cè)到具體的設(shè)備,幫我們自動(dòng)切換對(duì)應(yīng)的布局.
對(duì)同一套布局,系統(tǒng)會(huì)檢測(cè)到具體的設(shè)備和屏幕方向,遵循autoulayout的設(shè)定,自動(dòng)布局.
于是我們可以無(wú)視具體設(shè)備的尺寸和屏幕方向,根據(jù)size classes 的規(guī)定來(lái)布局了.
2) ?在ios8后,蘋果定義所有設(shè)備的size class只分為兩種屬性: 詳見(jiàn)枚舉UIUserInterfaceSizeClass
. 普通的(Regular)
. 緊湊的(Compact)
Any包含了上面兩種情況. 所以nib中的width:Any和height:Any代表:包含了所有情況.
如果在ios8后看到這種判斷設(shè)備和尺寸的代碼:就不太好(ios7時(shí)我剛?cè)腴Tios,看到我們公司項(xiàng)目中這么寫的,當(dāng)時(shí)沒(méi)有size classes)
//iphone4 and iphone4s
if (UIScreen.main.bounds.height == 480) {
}
//iphone5 and iphone5s
else if (UIScreen.main.bounds.height == 568) {
}
那么具體設(shè)備尺寸和 Compact,Regular的對(duì)應(yīng)關(guān)系是?
這張圖1-1來(lái)自于UITraitCollection的api文檔 https://developer.apple.com/reference/uikit/uitraitcollection

總結(jié)下:
1) ipad不管橫豎寬高size class都是regular,?
2) iPhone橫著時(shí)寬高size class都是compact,豎起來(lái)高會(huì)變成regular.
3)iPhone 6plus特殊點(diǎn),不管橫豎 長(zhǎng)邊就是regular,短邊是compact ?... iphone7不畫,因?yàn)樗叽绾蚷phone6s沒(méi)有變化.
現(xiàn)在ios10 nib編程已經(jīng)不用背這個(gè)了,為啥,見(jiàn)圖13-1. 它把尺寸和設(shè)備都形象化了
如果用代碼,我們還是要了解對(duì)應(yīng)關(guān)系的,不然我們獲取出來(lái)了屏幕width和height的size class,還不知道當(dāng)前設(shè)備是個(gè)啥情況呢.
2. trait - UITraitCollection和其接口UITraitEnvironment
上面說(shuō)取屏幕width和height的size class,如何取?取出來(lái)后,我們可以根據(jù)不同的尺寸和橫豎屏,更改布局,如隱藏或者顯示某些view等.
trait英文是特性的意思,我理解就是UITraitCollection.也就是關(guān)于尺寸,設(shè)備屏幕的一系列屬性的大集合.
https://developer.apple.com/reference/uikit/uitraitcollection 這是 UITraitCollection的官方文檔
UITraitCollection是接口UITraitEnvironment的屬性.它包含了設(shè)備關(guān)于屏幕的眾多trait,size class也是其中一個(gè)屬性:
1) 水平和豎直上的size class,horizontalSizeClass,verticalSizeClass
2) 顯示縮放比 displayScale
3) 用戶設(shè)備的分類 userInterfaceIdiom(枚舉UIUserInterfaceIdiom).
public enum UIUserInterfaceIdiom : Int {
case unspecified ?//未定義
case phone // iPhone and iPod touch style UI
case pad // iPad style UI
case tv // Apple TV style UI ?apple做電視和車載,我還沒(méi)看到大陸哪里有買哇-_-
case carPlay // CarPlay style UI
}
UIScreen, UIWindow, UIViewController, UIPresentationController,UIView都實(shí)現(xiàn)了UITraitEnvironment接口,因此可以很方便的直接取用:
self.traitCollection
兩個(gè)函數(shù),在trait發(fā)生改變時(shí)被調(diào)用:
1.public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) ?
它是接口UITraitEnvironment的方法.上面提到的UIScreen, UIWindow, UIViewController, UIPresentationController,UIView都可以實(shí)現(xiàn)它.當(dāng)trait發(fā)生改變時(shí),進(jìn)入這個(gè)方法.我們通過(guò)判斷其屬性改變布局:
代碼:
//MARK: UITraitEnvironment
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)? {
//iphone 橫屏
if previousTraitCollection?.horizontalSizeClass == .compact &&? previousTraitCollection?.verticalSizeClass == .compact{
print("iPhone從橫屏->豎屏")
}
else if (previousTraitCollection?.horizontalSizeClass == .compact &&? previousTraitCollection?.verticalSizeClass == .regular){
print("iPhone從豎屏->橫屏")
}
}
注意:1) previousTraitCollection是之前的設(shè)備狀態(tài),不是當(dāng)前的.而且有可能是nil,比如程序最開始啟動(dòng)的時(shí)候還沒(méi)有之前狀態(tài)喲
? ? ? ? 2) 橫豎屏切換記得先打開手機(jī)設(shè)置中的橫豎屏開關(guān)
2.public func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)
這個(gè)方法是接口UIContentContainer的方法,所幸UIViewController和UIPresentationController都實(shí)現(xiàn)了它. 它也是在trait改變時(shí)進(jìn)入的.它先于traitCollectionDidChange被調(diào)用.
可以在里面寫動(dòng)畫coordinator.animateAlongsideTransition ...
要調(diào)用super.willTransition(to: newCollection, with: coordinator). ?除非自己實(shí)現(xiàn)子viewcontroller的變化
代碼:
//MARK: UIContentContainer
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator){
super.willTransition(to: newCollection, with: coordinator)
coordinator.animateAlongsideTransition(in: self.view, animation: { (context) in
//動(dòng)畫,隨便寫點(diǎn)啥把.
self.btn.titleLabel?.text = "gugu"
}) { (context) in
//iphone 橫屏
if newCollection.horizontalSizeClass == .compact &&? newCollection.verticalSizeClass == .compact{
print("iphone目前是橫屏")
}
else if (newCollection.horizontalSizeClass == .compact &&? newCollection.verticalSizeClass == .regular){
print("iphone目前是豎屏")
}
}
}
3. 3D touch和trait的關(guān)系
啰嗦一點(diǎn),ios9中,在ViewController中判斷設(shè)備是否支持3Dtouch.就是這么寫的:
if(self.traitCollection.forceTouchCapability ==UIForceTouchCapabilityAvailable){
[self registerForPreviewingWithDelegate:self sourceView:self.view];
}
也是用到了trait的屬性哇.
要檢測(cè)3D touch是否被用戶關(guān)閉/打開了,也是在前面提到的函數(shù)里:
traitCollectionDidChange:
4. 不同的size class對(duì)應(yīng)不同的圖片尺寸
在橫屏,豎屏,以及各種寬高的size class組合下,一張圖片需要有不同的尺寸.
這個(gè)要是自己用代碼寫,會(huì)多么惡心,要適配多少情況啊,幸好image asset功能之一就是干這個(gè)事情的,后面的譯文會(huì)講到這個(gè),image asset有配置,可以對(duì)不同的size clas配置不同的圖片,見(jiàn)圖13-2
5. ViewController的present關(guān)系用到的size class的改變
這就是我掉坑的重點(diǎn),了解了何為size class后,vc間的present關(guān)系中,presented viewController會(huì)在不同的設(shè)備環(huán)境下有不同的表現(xiàn). 如在regular環(huán)境下(ipad)的popover窗體,到了compact環(huán)境下,會(huì)變成全屏,而我們可以通過(guò) UIViewController的屬性:popoverPresentationController來(lái)自定義presented viewController的變現(xiàn),讓它保持非全屏的模式.
譯文-終于開始翻譯了
原文地址:https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/BuildinganAdaptiveInterface.html
一個(gè)可適應(yīng)的界面應(yīng)該同時(shí)響應(yīng)trait和size的改變.在view controller這個(gè)層級(jí)上,用trait來(lái)大體決定你要顯示的內(nèi)容和控件們用到的layout. 例如:當(dāng)在size class之間切換時(shí),你會(huì)改變view的屬性,顯示或者隱藏一些view.
適應(yīng)trait的改變
trait可讓你在不同環(huán)境下配置不一樣的app顯示,大多數(shù)配置可以在storyboard上進(jìn)行,少數(shù)還需要代碼協(xié)助.
用storyboard來(lái)配置不同的Size Classes
在IB上使用size classes是很簡(jiǎn)單的,storyboard編輯器支持在不同的size classes上顯示界面.我們可以在特定的size classes上去掉某些view或者更改layout約束,你還可以創(chuàng)建image assets,把不同的image放在不同的size classes上.這樣,你就不用用好幾套代碼來(lái)做這種適配屏幕尺寸的事了,當(dāng)app的size class變化時(shí),UIKit會(huì)自動(dòng)更新對(duì)應(yīng)的界面.(很cool!)
ps:官網(wǎng)上了個(gè)老圖,為了與時(shí)俱進(jìn),上最新xcode8的界面

下面那一排設(shè)備,就是不同的size classes,是蘋果所有的設(shè)備尺寸,還可以選擇橫屏和豎屏
note: 沒(méi)安裝的view還是在你的view樹里面,還可以被操作到,只是不顯示罷了
(ps:不是很懂,這里的安裝是啥意思?是說(shuō)在size class-a下我加了個(gè)view,size class-b下我讓其不顯示,但是view還是在么?這樣的話,如果多來(lái)幾套布局,整個(gè)包不會(huì)很大嗎?嘻嘻,不過(guò)似乎也只能這樣子喲)
image assets 是個(gè)很好的存儲(chǔ)圖片資源的地兒,每個(gè)image asset都有一個(gè)圖片的多個(gè)版本,每個(gè)版本都有特定的配置.除了可以對(duì)普通屏和視網(wǎng)膜屏指定不同的圖片,還可以對(duì)橫屏和豎屏指定不同的圖片.只要在image asset里面配置的圖片,UIImageView能自動(dòng)選擇和當(dāng)前的size classes配置相符的圖片.(ps:這就是為啥image asset大行其道的原因吧,以前都是放圖在bunlde里,現(xiàn)在image asset 會(huì)保存多一張圖的多個(gè)版本,略占空間.但是打包發(fā)布到App Store上后,store會(huì)采用Slicing技術(shù),針對(duì)不同的設(shè)備生成不同的app變種,變種的不同圖片的選擇就是根據(jù)image asset來(lái)篩選的,所以實(shí)際上用戶設(shè)備中的app是不會(huì)很大的. 參考文檔:
1. https://developer.apple.com/library/prerelease/content/documentation/IDEs/Conceptual/AppDistributionGuide/AppThinning/AppThinning.html#//apple_ref/doc/uid/TP40012582-CH35-SW3
2. https://onevcat.com/2013/06/new-in-xcode5-and-objc/
)
圖13-2展示了image asset的配置,因?yàn)橛质抢蠄D,所以上個(gè)新的,好多屬性,重要是的是那個(gè)width class和height class,通過(guò)其組合可以對(duì)應(yīng)不同size classes下的圖片.下個(gè)帖子一一研究下

改變子view controller的Traits
默認(rèn)情況下,子viewController繼承父viewController的traits.不過(guò)trait和size classes一樣,不能要求所有子viewController都和父viewController完全一樣.例如:可能給子viewController的顯示空間沒(méi)有那么大,所以一個(gè)普通(regular)的父viewController會(huì)給它的幾個(gè)子viewController賦值緊湊(Compact)的size classe.
在容器view controller中,對(duì)父viewController調(diào)方法:setOverrideTraitCollection:forChildViewController: 來(lái)調(diào)整子viewController的trait
代碼13-1 展示如何創(chuàng)建trait并賦值給子view controller. 這段代碼寫父viewController中并最好只能執(zhí)行一次.子view controller的trait會(huì)一直保持,不被父viewController影響,直到它從view層中刪除.
UITraitCollection*horizTrait=[UITraitCollection
traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
UITraitCollection*vertTrait=[UITraitCollection
traitCollectionWithVerticalSizeClass:UIUserInterfaceSizeClassCompact];
UITraitCollection*childTraits=[UITraitCollection
traitCollectionWithTraitsFromCollections:@[horizTrait,vertTrait]];
[selfsetOverrideTraitCollection:childTraitsforChildViewController:self.childViewControllers[0]];
問(wèn)題來(lái)啦,trait屬性挺多的,被父viewController指定的屬性會(huì)在子viewController中一直保持,那么沒(méi)有指定的屬性呢?答案是遵循自父viewController的變化,和其保持一致.
如上面這段代碼,如果父viewController的水平size class發(fā)生變化,可是子viewController還是保持Regular
讓Presented View Controllers有新的風(fēng)格
知識(shí)儲(chǔ)備
先說(shuō)在present的意思,蘋果里面:
如果一個(gè)ViewController? A present了另一個(gè)ViewController? B, 則A稱為presentingViewController, B稱為presentedViewController. 它們通過(guò)UIViewController的這2個(gè)屬性可以訪問(wèn)到彼此.如圖1-2

實(shí)際上,presentedViewController是被presentingViewController retain了
ViewController之間的present關(guān)系是一種? 模態(tài) 的關(guān)系, B的view覆蓋在A的view上,用戶點(diǎn)不到A,只能操作B,想要關(guān)閉B,只能在B上提供按鈕或者navigation item?
在iPhone上present B后,默認(rèn)情況下,B的view就由自下往上的動(dòng)畫效果展示出來(lái),覆蓋在A的view上.
?動(dòng)畫效果是可以改的:presentedViewController的屬性modalTransitionStyle
view是覆蓋還是替換,還是自定義類似popover的局部的顯示??
用presentedViewController的屬性modalPresentationStyle來(lái)配置.這里說(shuō)下:
.FullScreen
默認(rèn)的機(jī)制,presentedViewController的view全屏顯示
presentingViewController還是屏幕的根viewcontroller,但是它的view直接被presentedViewController的view替換了.
.OverFullScreen
presentedViewController的view全屏顯示
presentingViewController的view還在原來(lái)的地方,只是被presentedViewController的view覆蓋了,如果presentedViewController的view設(shè)置了透明度,還能看到它在下面呢
ios8只支持.FullScreen和OverFullScreen
.PageSheet
presentedViewController的view全屏顯示
在iPad 豎屏上,它會(huì)略短一截,在statusbar的下面留出空白,ipad橫屏和iphone6 pluas上,它左右也會(huì)短一截,
present的函數(shù):
open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (@escaping () -> Swift.Void)? = nil)
storyboard的拉線:
Present Modally 和Present as popover ,如圖1-3

關(guān)閉的函數(shù)(這么說(shuō)其實(shí)不專業(yè),ios里的函數(shù)都是消息,發(fā)送給對(duì)象):
open func dismiss(animated flag: Bool, completion: (@escaping () -> Swift.Void)? = nil)
關(guān)閉時(shí),presentingViewController收到該消息,presentedViewController的view從屏幕上撤下,不再覆蓋presentingViewController的view,然后presentedViewController的內(nèi)存被釋放. 嘻嘻!別繞暈了,很簡(jiǎn)單的
值得一提的是:這個(gè)消息可以發(fā)送給presentingViewController也可以發(fā)送給presentedViewController. iOS的runtime會(huì)把這個(gè)消息最終傳遞到presentingViewController.所以,最終響應(yīng)該消息的是presentingViewController
扯起來(lái)的話,可說(shuō)的內(nèi)容還挺多的,雖然平時(shí)關(guān)閉一個(gè)vc,dismiss就萬(wàn)事大吉,面試時(shí)基本也不會(huì)有人問(wèn),不過(guò)多了解一點(diǎn)總是好的.-_-
1. 每個(gè)presentingViewController最多只能有一個(gè)presentedViewController.如果一個(gè)presentingViewController的presentedViewController已經(jīng)不為nil了.你再給它發(fā)送present(:animated:completion:)消息,則不會(huì)發(fā)生任何反應(yīng),completion handler也不會(huì)被調(diào)用.runtime會(huì)給你個(gè)警告
2. presentedViewController也能再present view controller, 這就形成了一個(gè)鏈,如果你對(duì)中間的B發(fā)送dismiss(:completion) 消息,則C會(huì)關(guān)閉掉,B不會(huì),因?yàn)榇丝趟彩且粋€(gè)presentingViewController ,如圖1-4

3. 如果給一個(gè)presentedViewController為nil的view controller發(fā)送dismiss(:completion) 消息,則不會(huì)發(fā)生任何事,但是completion handler會(huì)被調(diào)用
注意:
1) 默認(rèn)情況下,presentedViewController覆蓋presentingViewController的全部view
1) iOS8后,無(wú)論iPhone還是iPad都可讓presentedViewController只占據(jù)presentingViewController的一個(gè)subview的空間,而不是整個(gè)view區(qū)域
2) IOS7后,無(wú)論iPhone還是iPad都可讓presentedViewController只占據(jù)presentingViewController的一部分區(qū)域,而不是整個(gè)view區(qū)域
繼續(xù)翻譯
presentedViewController 會(huì)自適應(yīng)水平方向上的的regular和compact.比如:當(dāng)從水平的regular變成水平的compact時(shí),UIKit默認(rèn)自動(dòng)把ViewController的presentation style變成了UIModalPresentationFullScreen,自定義presentation style是由presentation controller進(jìn)行調(diào)整的.
啰嗦幾句:
presentation controller和presentation style是啥呢?
UIViewController有個(gè)擴(kuò)展,寫了2個(gè)只能get的屬性:
1) presentationController
2) popoverPresentationController? (它其實(shí)繼承于presentationController,是presentationController的popover的擴(kuò)展)
它們不是繼承于UIViewController的,而是繼承于NSObject的.它們負(fù)責(zé)管理一個(gè)UIViewController的present的某些屬性.
注意:
一定要先給UIViewController的modalPresentationStyle屬性賦值,上面2個(gè)屬性才不會(huì)是nil的.
1) 如果modalPresentationStyle是.popover,則popoverPresentationController被賦值
2) 如果modalPresentationStyle是其他枚舉值,則presentationController被賦值
繼續(xù)翻譯
對(duì)于某些app,全屏的present會(huì)造成困擾.比如,如果點(diǎn)擊一個(gè)popover的bounds的外面空白地方,會(huì)關(guān)掉這個(gè)popover,但是如果這個(gè)popover已經(jīng)占領(lǐng)了全屏,就沒(méi)法關(guān)閉它了,就如13-3所示.如果默認(rèn)的自適應(yīng)style機(jī)制不能滿足你的需要,你應(yīng)該告知UIKit用不同的style來(lái)present

如何自定義presenting style呢? 首先得給presentedViewController的presentationController或者popoverPresentationController設(shè)置delegate,當(dāng)自適應(yīng)變化開始時(shí),delegate的方法會(huì)被觸發(fā).它會(huì)返回不同的presenting style,它還可以讓presentationController交替顯示不同的ViewController(-_- delegate略強(qiáng)大)
在delegate中實(shí)現(xiàn)UIAdaptivePresentationControllerDelegate的代理方法:
optional public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle
頭文件的注釋道:在ios8里,只支持2個(gè)style
UIModalPresentationFullScreen:全屏,
UIModalPresentationOverFullScreen.
注意吧.官網(wǎng)上寫的是:adaptivePresentationStyleForPresentationController:?如果真直接copy過(guò)去實(shí)現(xiàn)這個(gè),就完蛋了,我就栽在這里了.
代碼:
//MARK: UIPopoverPresentationControllerDelegate
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle{
return .none
}
當(dāng)app轉(zhuǎn)入compact的環(huán)境時(shí),只支持全屏或者 無(wú) (UIModalPresentationNone),返回.none就是告訴presentation controller忽略compact的環(huán)境,保持之前的present style,在任何設(shè)備上都如此,13-4展示了全屏和非全屏的效果,以做比較:

delegate的另一個(gè)方法
public func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController?
當(dāng)進(jìn)入了compact環(huán)境時(shí),你可以在你的view層次里insert一個(gè)navigation controller,或者load一個(gè)新的view controller,來(lái)替換本該present的view controller
(不好意思,在iphone6上沒(méi)調(diào)試出來(lái),實(shí)現(xiàn)了沒(méi)進(jìn)入過(guò)該函數(shù),我還要進(jìn)一步研究)
實(shí)現(xiàn)適應(yīng)性彈出框(Popover)的建議
當(dāng)從水平Regular變成水平Compact環(huán)境時(shí),Pop over 需要額外的改動(dòng).水平Compact環(huán)境下,默認(rèn)的行為是popover會(huì)變成全屏.因?yàn)閜opover的關(guān)閉方法是點(diǎn)擊它bounds之外的空白處,而全屏就無(wú)法做到這一點(diǎn).我們可以通過(guò)下面的做法來(lái)改變這種情況:
1) 讓popover到navigation的棧里面
加一個(gè)navigation controller,通過(guò)navigationBarItem來(lái)dismiss它
2) 加控件來(lái)dismiss全屏的popover
可以通過(guò)加控件來(lái)dismiss全屏的popover,但是更多的辦法是用navigation controller換掉popover, 函數(shù)是:
presentationController:viewControllerForAdaptivePresentationStyle:
用navigation controller給你的模態(tài)界面加上一個(gè)Done按鈕或者其他控件來(lái)dismiss掉這個(gè)界面.
3) 用presentation controller的delegate消滅一切系統(tǒng)的的自適應(yīng)
這就是網(wǎng)上大多數(shù)的帖子做法,他們經(jīng)常通過(guò)簡(jiǎn)潔的方式,直接指導(dǎo)你實(shí)現(xiàn)步驟,但是不說(shuō)為啥,對(duì)看帖子的人來(lái)說(shuō),提升不大,換而言之,看的人應(yīng)該多思考多查閱,不能讓別人把什么都擺在你面前,那也做不到.除非是時(shí)間來(lái)不及,項(xiàng)目逼死人.--說(shuō)給自己聽的!自勉!
作為popover的UIViewController,設(shè)置它的屬性popoverPresentationController的delegate
實(shí)現(xiàn)delegate的func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle方法,返回:UIModalPresentationNone (.none) 更多的信息,請(qǐng)參考:https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/BuildinganAdaptiveInterface.html#//apple_ref/doc/uid/TP40007457-CH32-SW6
對(duì)Size 的變化做出反應(yīng)
size會(huì)因?yàn)楹芏嘣虬l(fā)生變化,包括下面的:
1) 窗口的尺寸變化,大多數(shù)時(shí)候由轉(zhuǎn)動(dòng)屏幕引起
2) 父view controller 重新設(shè)置了子view controller的尺寸
3) presentationController改變了它顯示的ViewController的尺寸
當(dāng)size發(fā)生變化時(shí),UIKit根據(jù)layout的約束,自動(dòng)改變當(dāng)前顯示的view controller層級(jí)的的尺寸和位置,如果你使用auto layout來(lái)指定view的size和位置,你的app會(huì)自動(dòng)適應(yīng)任何size和設(shè)備帶來(lái)的變化.
如果你的autoulayout 還不足以達(dá)到你所想要的效果, 你可以用viewWillTransitionToSize:withTransitionCoordinator: 來(lái)改變你的布局,你可以創(chuàng)建額外的動(dòng)畫,這些動(dòng)畫會(huì)和size-change的動(dòng)畫一起運(yùn)行.例如:當(dāng)轉(zhuǎn)屏?xí)r,調(diào)用UIViewControllerTransitionCoordinator的屬性targetTransform?來(lái)創(chuàng)建一個(gè)反轉(zhuǎn)矩陣給某些需要的顯示控件使用
demo:https://github.com/ivychenyucong/TestPopover
參考: http://www.cnblogs.com/zhw511006/p/3998534.html ? 對(duì)ios8的size classes講解的很清楚到位
https://onevcat.com/2014/07/ios-ui-unique/ ?size class的解釋很清楚