翻譯:Building an Adaptive Interface-翻譯-size class和present view controller

寫在之前的大篇廢話

研究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:

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 ?都不用

效果:

1-1-2


翻譯之前的儲(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


1-1

總結(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的方法,所幸UIViewControllerUIPresentationController都實(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的界面


13-1

下面那一排設(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è)帖子一一研究下


13-2

改變子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


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

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


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

13-3 popover從regular變成compact時(shí),popover變成了全屏

如何自定義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展示了全屏和非全屏的效果,以做比較:


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的解釋很清楚

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容