更好的閱讀體驗,請到個人博客閱讀: iOS中的系統轉場
請忽略標題,??,本文記錄的是對下圖所示的Kind, Presentation, Transition的理解:
目錄如下:
<h1 id="size-classes">Size Classes</h1>
開始之前,先來了解一下Size Classes, 從iOS8.0開始引入了Size Classes的概念來對屏幕進行分類:把各個設備屏幕以及它們的屏幕旋轉狀態都抽象成屏幕Size的變化,下表是不同尺寸設備屏幕對Size Classes的約定。
Device | Portrait | Landscape |
---|---|---|
iPad (all)</br>iPad Mini | Vertical size class: Regular</br>Horizontal size class: Regular | Vertical size class: Regular</br>Horizontal size class: Regular |
iPhone 6 Plus | Vertical size class: Regular</br>Horizontal size class: Compact | Vertical size class: Compact</br>Horizontal size class: Regular |
iPhone 6 | Vertical size class: Regular</br>Horizontal size class: Compact | Vertical size class: Compact</br>Horizontal size class: Compact |
iPhone 5s</br>iPhone 5c</br>iPhone 5 | Vertical size class: Regular</br>Horizontal size class: Compact | Vertical size class: Compact</br>Horizontal size class: Compact |
iPhone 4s | Vertical size class: Regular</br>Horizontal size class: Compact | Vertical size class: Compact</br>Horizontal size class: Compact |
寬(正常,任意, 緊湊),高(正常,任意, 緊湊),3 x 3 共 9 種 Size,每種 Size 都可以設置特定的一套布局,如果不特殊指定,默認是在(寬任意,高任意)模式下設置,且其他 8 種布局繼承它。對于Size Classes,可以看看博客: iOS8 Size Classes初探
<h1 id="presentation-kind">Kind-轉場類型</h1>
先說明一下兩個名詞,假如view controller A模態展示了view controller B,則
- presenting view controller, 是上述場景中的A view controller, 它會負責展示另外一個view controller;
- presented view controller, 是上述場景中的B view controller,它是由其它控制器展示出來的view controller。
下圖是Xcode8.0轉場Kind的設置選項,可以看到系統默認的類型有四種,Show, Show Detail, Present Modally, Present As Popover。
<h2 id="presentation-kind-show">Show</h2>
Presents a view controller in a primary context.
func show(_ vc: UIViewController, sender: Any?)
使用該方法可以將display view controller從presenting that view controller on screen的過程中解耦,ViewController不需要知道它是嵌入到UINavigationController中或者是UISplitViewController中,都可以調用此方法來展示到屏幕上。UISplitViewController和UINavigationController重寫了該方法,使其能夠根據自身設計來處理展現過程。例如,UINavigationController重寫了該方法,并使用該方法來push viewcontroller到navigation stack中。
該方法的默認實現會調用targetViewControllerForAction:sender:在view controller層級中找到重寫該方法的對象,然后調用該對象的此方法,使其能夠以適當的方式來展現view controller。 如果targetViewControllerForAction:sender:方法返回nil,此方法會使用window的root view controller來模態顯示vc。
你可以在自定義view controller中重寫該方法來展示vc,你的實現應該在regular和compact環境中做好適配。
<h2 id="presentation-kind-showdetail">Show Detail</h2>
Presents a view controller in a secondary (or detail)?? context.
func showDetailViewController(_ vc: UIViewController, sender: Any?)
該方法特點同Show類似:使用該方法可以將display view controller從presenting that view controller on screen的過程中解耦,ViewController不需要知道它是嵌入到UINavigationController中或者是UISplitViewController中,都可以調用此方法來展示到屏幕上。在regular環境,UISplitViewController重寫了該方法,將vc以detail view controller的方式加載;在compact環境,split view controller的該方法實現是調用show(_:sender:)
方法。
同show(_:sender:)
相同:該方法的默認實現會調用targetViewControllerForAction:sender:在view controller層級中找到重寫該方法的對象,然后調用該對象的此方法,使其能夠以適當的方式來展現view controller。 如果targetViewControllerForAction:sender:方法返回nil,此方法會使用window的root view controller來模態顯示vc。
你可以在自定義view controller中重載此方法來展示vc,使用此方法來將vc展現在secondary context中,例如,一個容器view controller可能會使用此方法來替換第二子控制器,你的實現應該為regular和compact環境做好適配.
<h2 id="presentation-kind-present-modally">Present Modally</h2>
Presents a view controller modally.
- (void)presentViewController:(UIViewController *)viewControllerToPresent
animated:(BOOL)flag
completion:(void (^)(void))completion;
在horizontally regular的環境,view controller會以modalPresentationStyle屬性設定的樣式進行展現。在horizontally compact環境,view controller默認會以全屏的方式進行展現。對于viewControllerToPresent關聯的presentation controller, 如果你實現了其adaptive delegate方法,則可以動態修改presentation style。
調用該方法的對象不一定是處理該presentation的對象。每一個presentation style會有不同的規則來處理其行為。例如,full-screen presentation必須由自身覆蓋了整個屏幕的view controller來處理。如果調用該方法的當前view controlelr不能滿足該要求,則它會沿著view controller的層級向上傳遞給最近的父控制器,由其父控制器來負責處理或者繼續轉發該請求。
在展現view controller之前,該方法會基于presentation style來設置presented view controller的view大小。對于大多數的presentation styles,最終視圖會以presented view controller的modalTransitionStyle屬性設定的轉場樣式動畫來顯示到屏幕上。對于自定義的presentations, 視圖會以presented view controller的transitioning delegate中要求的動畫來展示。對于current context presentations,視圖可能會是以當前view controller的transition style來顯示到屏幕上,這種情況稍后會再次提到。
注意: completion閉包是在presented view controller的viewDidAppear:的方法調用完成后才會調用。
接下來聊聊present modally類型下的presentation style.
<h3 id="presentation-style">Presentation Styles</h3>
view controller的presentation style控制其在屏幕上的顯示。UIKit定義了多種標準的presentation styles, 每一種都有特定的UI展示和用途。當然,你也可以自定義presentation styles。當設計你的app時,根據當前的使用場景選擇最合適的presentaion style, 然后設置presented view controller的modalPresentationStyle屬性值為對應的常量值。
modalPresentationStyle:
The presentation style determines how a modally presented view controller is displayed onscreen. In a horizontally compact environment, modal view controllers are always presented full-screen. In a horizontally regular environment, there are several different presentation options. For a list of possible presentation styles, and their compatibility with the available transition styles, see the UIModalPresentationStyle constant descriptions.
UIModalPresentationStyle的枚舉值定義如下:
public enum UIModalPresentationStyle : Int {
case fullScreen
@available(iOS 3.2, *)
case pageSheet
@available(iOS 3.2, *)
case formSheet
@available(iOS 3.2, *)
case currentContext
@available(iOS 7.0, *)
case custom
@available(iOS 8.0, *)
case overFullScreen
@available(iOS 8.0, *)
case overCurrentContext
@available(iOS 8.0, *)
case popover
@available(iOS 7.0, *)
case none
}
由于popover需要進行特殊處理,在Xcode中,Presentation處僅有如下圖所示的設置:
對以上枚舉值進行大體分類: full-screen presentation styles, popover style, current context styles, custom presentation styles。其中:
- full-screen presentation styles對應枚舉值fullScreen, pageSheet, formSheet, overFullScreen;
- popover style對應枚舉值popover;
- current context styles對應枚舉值currentContext, overCurrentContext。
先看看full-screen presentation styles。
<h4 id="full-screen-presentation">Full-Screen Presentation Styles</h4>
Full screen presentation styles cover the entire screen, preventing interactions with the underlying content. In a horizontally regular environment, only one of the full-screen styles covers the underlying content completely. The rest incorporate dimming views or transparency to allow portions of the underlying view controller to show through. In a horizontally compact environment, full-screen presentations automatically adapt to the UIModalPresentationFullScreen style and cover all of the underlying content.
full screen presentation styles會覆蓋設備的整個屏幕,阻止用戶與底部內容進行交互。在horizontally regular環境,full screen presentation styles中只有一種類型(UIModalPresentationFullScreen)會完全覆蓋住底部的內容,其余的類型會結合透明或者黑色半透明的視圖來使底部的部分內容顯示出來。在horizontally compact環境,所有的full screen presentation styles會自動適配為UIModalPresentationFullScreen類型并覆蓋底部的所有內容。
下圖是在horizontally regular環境下fullScreen, pageSheet, formSheet樣式下的顯示效果。圖中,左上角的綠色view controller會presents右上角的藍色view controller,對于某些presentation styles, UIKit會在兩個view controller之間插入dimming view。

fullScreen
case fullScreen = 0
A presentation style in which the presented view covers the screen. The views belonging to the presenting view controller are removed after the presentation completes.
此種類型的presentation style presented view會完全覆蓋屏幕。且屬于presenting view controller的視圖會在presentation completes后移除視圖層級。
注意:當你使用UIModalPresentationFullScreen樣式來presenting a view controller的時候,UIKit會在轉場動畫完成后移除底部控制器(所覆蓋的控制器)的視圖,如果你想阻止視圖的移除,則可以使用UIModalPresentationOverFullScreen樣式來替代。當presented view controller設計有透明區域以顯示底層內容時,你可能會用到這種樣式。
pageSheet
case pageSheet = 1
In a horizontally regular environment, a presentation style that partially covers the underlying content. The presented view'??s width is set to the width of the screen in a portrait orientation and the the height is set to the height of the screen. Any uncovered areas are dimmed to prevent the user from interacting with them. (In portrait orientations, this option is essentially the same as fullScreen.)
In a horizontally compact environment, this option behaves the same as fullScreen.
在horizontally regular環境,此種presentation style會部分遮擋底部的內容。presented view的寬度會被設置成與portrait orientation模式下屏幕的寬度相等,且presented view的高度被設置成與設備當前模式(portrait 或者 landscape)下屏幕的高度相同。任何未覆蓋到的區域會加黑色透明視圖以阻止用戶的交互。在portrait orientations場景,該設置大體上與fullScreen相同(該樣式下,statusbar上會覆蓋黑色透明視圖)。在horizontally compact環境下,該樣式與fullScreen樣式完全相同。
formSheet
case formSheet = 2
In a horizontally regular environment, a presentation style that displays the content centered in the screen. The width and height of the content area are smaller than the screen size and a dimming view is placed underneath the content. If the device is in a landscape orientation and the keyboard is visible, the position of the view is adjusted upward so that the view remains visible. All uncovered areas are dimmed to prevent the user from interacting with them.
In a horizontally compact environment, this option behaves the same as fullScreen.
在horizontally regular環境下,formSheet這種樣式會將內容布局在屏幕的中間。顯示內容的寬高都會比屏幕尺寸小,并且會在顯示內容下插入黑色透明視圖(dimming view)。如果設備處于landscape orientation并且彈出了鍵盤,顯示內容會向上移動適配以使顯示內容仍然可見。所有未被顯示內容覆蓋到的區域都會被dimmed以阻止用戶與其交互。
在horizontally compact環境,該選項的效果與fullScreen的效果完全一致。
When using one of the full-screen presentation styles, the view controller that initiates the presentation must itself cover the entire screen. If the presenting view controller does not cover the screen, UIKit walks up the view controller hierarchy until it finds one that does. If it can’t find an intermediate view controller that fills the screen, UIKit uses the root view controller of the window.
當你使用這些full-screen presentation styles時,啟動這次presentation的view controller必須其自身是覆蓋了整個屏幕。如果presenting view controller沒有覆蓋整個屏幕,則UIKit會沿著view controller的層級一直尋找直到找到滿足該條件的view controller,如果沒有找到,UIKit則會使用window的root view controller。
<h4 id="popover">The Popover Style</h4>
內容見Presenting a View Controller in a Popover
<h4 id="current-context">The Current Context Styles</h4>
使用UIModalPresentationCurrentContext樣式會使presented view controller覆蓋你在UI中指定的view controller。你通過設置view controller的definesPresentationContext屬性值為YES來使其成為被覆蓋的對象。下圖演示的是curren context presentation僅僅覆蓋了split view controller的一個子控制器。

注意:使用該樣式時,對于未被presented view覆蓋的區域,用戶仍然可以與UI元素(例如UIButton)進行交互。
A presentation style where the content is displayed over a view controller’s content whose definesPresentationContext property is true. UIKit may walk up the view controller hierarchy to find a view controller that wants to define the presentation context. The views belonging to the presenting view controller are removed after the presentation completes.
When presenting a view controller in a popover, this presentation style is supported only if the transition style is coverVertical. Attempting to use a different transition style triggers an exception. However, you may use other transition styles (except the partial curl transition) if the parent view controller is not in a popover.
該樣式會將內容顯示在屬性值definesPresentationContext為true的view controller之上。UIKit會沿著view controller的層級向上尋找定義了presentation context的view controller。屬于presenting view controller的視圖會在presentation completes后移除。
當在一個popover中來presenting view controller時,只有當transition style是coverVertical時才支持此種樣式,使用其它類型的transition style會導致crash(經過測試,只有當transition styles為partial curl時才會crash,不過小心為妙)。當parent view controller不在popover中時,你可以使用除partial curl外的其它transition styles。
當轉場結束時,屬于presented view controller的視圖會被移除,你可以使用UIModalPresentationOverCurrentContext樣式來替代以避免被移除,你可能會在當presented view controller有透明區域使底部內容顯示出來時使用此樣式。
定義成為presentation context的view controller, 也可以在其內定義presentation時使用的轉場動畫。通常情況下,UIKit會使用presented view controller的modalTransitionStyle屬性定義的動畫將presented view controller顯示到屏幕上。如果presentation context view controller的屬性providesPresentationContextTransitionStyle值為YES,則UIKit會使用該view controller的modalTransitionStyle屬性值來執行動畫。
當轉換到horizontally compact環境時,current context樣式會適配為UIModalPresentationFullScreen樣式。如果要想改變這種適配行為,可以使用adaptive presentation delegate來指定另一種presentation style或者更換view controller。
<h4 id="custom-presentation">Custom Presentation Styles</h4>
UIModalPresentationCustom允許你使用自定義的樣式來展示view controller。創建自定義的樣式需要你子類化UIPresentationController并且使用其方法來將自定義視圖動畫顯示到屏幕,定義其大小和設置presente view controller的大小。
更多信息參考: Creating Custom Presentations
<h3 id="transiton-style">Transition Styles</h3>
Transition styles determine the type of animations used to display a presented view controller. For the built-in transition styles, you assign one of the standard transition styles to the modalTransitionStyle property of the view controller you want to present. When you present the view controller, UIKit creates the animations that correspond to that style. For example, Figure 8-4 illustrates how the standard slide-up transition (UIModalTransitionStyleCoverVertical) animates the view controller onscreen. View controller B starts offscreen and animates up and over the top of view controller A. When view controller B is dismissed, the animation reverses so that B slides down to reveal A.
transition styles決定了顯示presented view controller時所用的動畫類型。對于系統內置的transition styles,你可以通過對presented view controller的modalTransitionStyle屬性賦值來指定樣式。當你顯示view controller時,UIKit屬性值對應的動畫。例如,下圖演示了系統UIModalTransitionStyleCoverVertical樣式對應的顯示動畫。view controller B從不可見->往上滑進屏幕->覆蓋view controller A。當view controller B退場時,它會向下滑出屏幕以顯示view controller A。

You can create custom transitions using an animator object and transitioning delegate. The animator object creates the transition animations for placing the view controller onscreen. The transitioning delegate supplies that animator object to UIKit at the appropriate time. For information about how to implement custom transitions, see Customizing the Transition Animations
目前iOS支持的系統轉場類型如下圖所示:
經過測試,Partial Curl轉場只有在Presentation為Full Screen時才會有效,其余presentation styles會導致crash,不知道是我使用的方式不對或者是其它原因,請指教。
<h2 id="presenting-popover">Presenting a View Controller in a Popover</h2>
同上,這種方式也是使用api:
- (void)presentViewController:(UIViewController *)viewControllerToPresent
animated:(BOOL)flag
completion:(void (^)(void))completion;
來展現視圖。與上不同的是,popover的展現方式還需要額外的配置。在設置 model presentation style值為UIModalPresentationPopover之后,配置如下與popover相關的屬性值:
- 設置presented view controller的preferredContentSize,將視圖大小設置為期望的大小;
- 從presented view controller的popoverPresentationController屬性中獲取關聯的UIPopoverPresentationController對象,然后設置該對象的錨點。設置錨點可從如下兩種方式中任選一種:
- 設置barButtonItem屬性值為一個bar button item;
- 設置sourceView和sourceRect屬性值為視圖中的特定區域;
你可以使用UIPopoverPresentationController對象來修改popover最終繪制出來的效果。popover presentation controller同樣也支持使用一個delegate對象來監聽presentation過程以做出對應的響應。例如,你可以使用該delegate對象來監聽popover的appears, disappears, 或者repositioned on the screen事件,以做出對應的響應。關于該對象的更多信息,請參考: UIPopoverPresentationController Class Reference
UIModalPresentationPopover樣式會以浮層的形式顯示view controller.Popovers可用來顯示一些額外信息,或者,是當前聚焦或者選中對象關聯的對象列表。在horizontally regular環境,彈出的浮層只會覆蓋屏幕的一部分,如下圖所示。在horizontally compact環境,popovers會默認適配為UIModalPresentationOverFullScreen樣式。在浮層以外的區域點擊則會自動使浮層消失。

由于popovers在horizontally compact環境下會自動適配為full-screen presentations,你需要編輯你的浮層代碼以處理這種適配。在full-screen模式下,你需要一種方式來使presented popover消失。你可以添加一個按鈕,將popover嵌入到可dismissible的容器視圖控制器中,或者是修改該適配行為。