UITraitCollection


UITraitCollection

為表征 size class 而生,用來區分設備。你可以在它身上獲取到足以區分所有設備的特征。


UITraitEnvironment 協議、UIContentContainer 協議

UIViewController 遵循了這兩個協議,用來監聽和設置 traitCollection 的變化。

@protocol UITraitEnvironment <NSObject>

@property (nonatomic, readonly) UITraitCollection *traitCollection NS_AVAILABLE_IOS(8_0);

/*! To be overridden as needed to provide custom behavior when the environment's traits change. */
- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection NS_AVAILABLE_IOS(8_0);
@end   

UIViewController 對 UIContentContainer 協議提供了默認的實現。我們自定義 ViewController 的時候可以重寫這些方法來調整視圖布局,比如我們可以在這些方法里調整 ChildViewControler 的位置。當我們重寫這些協議方法時,我們通常都去調用 super。

@protocol UIContentContainer <NSObject>

preferredContentSize 在 UIContentContainer 協議中是只讀的,對應的 UIViewController 有可寫的版本。我們可以使用 preferredContentSize 來設置我們期望的 ChildViewController 的界面大小。舉個例子,如果應用中使用的 popOver 大小會發生變化,iOS7 之前我們可以用 contentSizeForViewInPopover 來調整。iOS7 開始這個 API 被廢棄,我們可以使用 preferredContentSize 來設置。

當一個容器 ViewController 的 ChildViewController 的這個值改變時,UIKit 會調用 preferredContentSizeDidChangeForChildContentContainer 這個方法告訴當前容器 ViewController 。我們可以在這個方法里根據新的 Size 對界面進行調整。

@property (nonatomic, readonly) CGSize preferredContentSize NS_AVAILABLE_IOS(8_0);

- (void)preferredContentSizeDidChangeForChildContentContainer:(id <UIContentContainer>)container NS_AVAILABLE_IOS(8_0);

/*
Intended as a bridge for a view controller that does not use auto layout presenting a child that does use auto layout.

If the child's view is using auto layout and the -systemLayoutSizeFittingSize: of the view
changes, -systemLayoutFittingSizeDidChangeForChildContentContainer: will be sent to the view controller's parent.
*/
- (void)systemLayoutFittingSizeDidChangeForChildContentContainer:(id <UIContentContainer>)container NS_AVAILABLE_IOS(8_0);

/*
When the content container forwards viewWillTransitionToSize:withTransitionCoordinator: to its children, it will call this method to determine what size to send them. 

If the returned size is the same as the child container's current size, viewWillTransitionToSize:withTransitionCoordinator: will not be called.

設置 ChildViewController 的 size。當容器ViewControllerviewWillTransitionToSize:withTransitionCoordinator:被調用時(我們重寫這個方法時要調用 super),sizeForChildContentContainer 方法將會被調用。然后我們可以把需要設置的 size 發送給 ChildViewController。當我們設置的這個 size 和當前 ChildViewController 的 size 一樣,那么 ChildViewController 的 viewWillTransitionToSize 方法將不會被調用。默認的實現是返回 parentSize。
*/
- (CGSize)sizeForChildContentContainer:(id <UIContentContainer>)container withParentContainerSize:(CGSize)parentSize NS_AVAILABLE_IOS(8_0);

/* 
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized). 

If you override this method, you should either call super to propagate the change to children or manually forward the change to children.

ViewController 的 View 的 size 被他的 Parent Controller 改變時,會觸發這個方法。(比如rootViewController 在它的 window 旋轉的時候)。我們在重寫這個方法時,確保要調用 super,來保證 size 改變的這條消息能夠正常傳遞給它的 Views 或者 ChildViewControllers。

*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0);

/* 
This method is called when the view controller's trait collection is changed by its parent.

If you override this method, you should either call super to propagate the change to children or manually forward the change to children.

當 ViewController 的 traitCollection 的值將要改變時會調用這個方法。這個方法是在  UITraitEnvironment 協議方法 traitCollectionDidChange: 之前被調用。我們在重寫這個方法時,也要確保要調用 super 來保證消息的傳遞。
*/
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0);

@end 	  

iOS 8 的 UIKit 中大多數 UI 的基礎類 (包括 UIScreen,UIWindow,UIViewController 和 UIView) 都實現了 UITraitEnvironment 這個協議,通過其中的 traitCollection 這個屬性,我們可以拿到對應的 UITraitCollection 對象,從而得知當前的 Size Class,並進一步確定界面的布局。

和 UIKit 中的響應者鏈正好相反,traitCollection 將會在 view hierarchy 中自上而下地進行傳遞。對於沒有指定 traitCollection 的 UI 部件,將使用其父節點的 traitCollection。這在布局包含 childViewController 的界面的時候會相當有用。(還沒遇到過,很少使用 addChild )

對於 ViewController 來說的話,后者也許是更好的選擇,因為提供了轉場上下文方便進行動畫;但是對於普通的 View 來說就只有前面一個方法了,然后在其中對當前的 traitCollection 進行判斷,並進行重新布局以及動畫。代碼看起來大概會是這個樣子:

- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection 
          withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator {   
          
	[super willTransitionToTraitCollection:newCollection  withTransitionCoordinator:coordinator];
	[coordinator animateAlongsideTransition:^(id <UIViewControllerTransitionCoordinatorContext> context) {
    	if (newCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact) {
        	//To Do: modify something for compact vertical size
    	} else {
        	//To Do: modify something for other vertical size
    	}
    	[self.view setNeedsLayout];
	} completion:nil];   
}

另外,UIViewController還另外提供了以下兩個方法:

- (void)setOverrideTraitCollection:(UITraitCollection *)collection forChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);
  
- (UITraitCollection *)overrideTraitCollectionForChildViewController:(UIViewController *)childViewController NS_AVAILABLE_IOS(8_0);  

我們可以通過調用ViewController的setOverrideTraitCollection方法為它的ChildViewController重新設置traitCollection的值。一般情況下traitCollection值從父controller傳到子controller是不做修改的。當我們自己實現一個容器Controller的時候,我們可以使用這個方法進行調整。

相對的,我們可以通過overrideTraitCollectionForChildViewController方法獲得ChildViewController的traitCollection值。

摘自:
iOS 中的 UI 自適應
WWDC 2014 Session筆記 - iOS界面開發的大一統


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM