Shutterbug是斯坦福大學的Paul Hegarty教授在iOS 7開發教學視頻中給出的一個示例。Shutterbug訪問Flickr的最新圖片列表,並顯示具體的圖片,同時支持iPhone和iPad。具體地參見網易視頻:表格視圖和iPad
視頻中Hegarty教授使用Xcode 6進行代碼演示,為了支持iPhone和iPad同時建立了兩套Storyboard,即Universal App。在看這個視頻的時候,我裝的是Xcode 7。Xcode 7支持在一套Storyboard中創建Universal App,這主要是得益於 Size Classes和Auto Layout 特性,具體的可以看下2014 WWDC視頻Building Adaptive Apps with UIKit。並且從iOS 8後UISplitViewController可以同時在iPhone和iPad中使用。因此,我想用新的特性去實現這個例子。當然,期間遇到了不少問題,走了不少彎路。在此,我將我遇到的問題分享給大家,以避免犯同樣的錯誤。
官方給出的UISplitViewController的介紹
The UISplitViewController class is a container view controller that presents a master-detail interface. In a master-detail interface, changes in the primary view controller (the master) drive changes in a secondary view controller (the detail). The two view controllers can be arranged so that they are side-by-side, so that only one at a time is visible, or so that one only partially hides the other. In iOS 8 and later, you can use the UISplitViewController class on all iOS devices; in previous versions of iOS, the class is available only on iPad.
它包含兩個視圖控制器,一個是主視圖控制器,另一個是詳細視圖控制器。共有三種顯示模式:
UISplitViewControllerDisplayModePrimaryHidden
The primary view controller is hidden.
UISplitViewControllerDisplayModeAllVisible
The primary and secondary view controllers are displayed side-by-side onscreen.
UISplitViewControllerDisplayModePrimaryOverlay
The primary view controller is layered on top of the secondary view controller, leaving the secondary view controller partially visible.
還有一種叫UISplitViewControllerDisplayModeAutomatic,其實就是讓UISplitViewController根據當前的Size Classes選擇上述的三種顯示模式。真正的顯示模式應該只有三種。
在UISplitViewControllerDisplayModeAutomatic 模式下,UISplitViewController會根據水平方向的size class選擇顯示模式:
UIUserInterfaceSizeClassCompact對應UISplitViewControllerDisplayModePrimaryHidden模式
UIUserInterfaceSizeClassRegular對應UISplitViewControllerDisplayModeAllVisible模式
UISplitViewControllerDisplayModePrimaryOverlay對應UISplitViewControllerDisplayModePrimaryHidden模式時以Popover形式顯示主視圖。
具體的可以參考官方文檔或者看下NShipster中的這篇文章。
UISplitViewControllerDelegate的幫助文檔參見蘋果文檔。這裡我介紹下程序中用到的兩個委托方法。
-(BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController -(void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode
第一個方法當詳細視圖折疊到主視圖時調用,也就是對應的UISplitViewControllerDisplayModePrimaryHidden模式。第二個方法當顯示模式發生變化時調用。下面結合著程序中遇到的問題,介紹下這兩個方法的使用。
1)由誰來充當委托對象
我查了些資料,這個沒有定論,得結合自己的實際情況。
Building Adaptive Apps with UIKit這個視頻給出的例子中,由程序委托充當委托。
詳細視圖控制器充當委托。這種情況,我在Stackoverflow上看到的比較多。
通過擴展。NShipster中的這篇文章就是這種方式。
由UISplitViewController的子類充當。程序中我采用的是這種方式。
2)何時設置UISplitViewController的委托
如果你是采用的Storyboard或xib文件,建議在awakeFromNib裡設置。在查閱資料的時候發現有些人遇到了委托方法沒觸發的問題。大家都建議盡早的設置委托。
3)當在iPhone上豎屏顯示時,詳細視圖默認Push到了主視圖之上
解決這個問題可以用以下代碼:
-(BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController { if ([secondaryViewController isKindOfClass:[UINavigationController class]]) { UINavigationController *nav = (UINavigationController *)secondaryViewController; if ([nav.topViewController isKindOfClass:[ImageViewController class]]) { ImageViewController *controller = (ImageViewController *)nav.topViewController; if (!controller.imageURL) { return YES; } } } return NO; }
該方法返回一個BOOL值,如果返回YES表示由我們自己處理,反之交給UISplitViewController,它會采用默認的處理方式處理。
4)在iPad裡,由橫屏切換為豎屏時,詳細視圖左側沒有barButtonItem
解決這個問題可以用以下代碼:
-(void)splitViewController:(UISplitViewController *)svc willChangeToDisplayMode:(UISplitViewControllerDisplayMode)displayMode { UIViewController *primaryViewController = svc.viewControllers[0]; UIViewController *secondaryViewController = svc.viewControllers[1]; if ([secondaryViewController isKindOfClass:[UINavigationController class]]) { UINavigationController *nav = (UINavigationController *)secondaryViewController; if ([nav.topViewController isKindOfClass:[ImageViewController class]]) { ImageViewController *controller = (ImageViewController *)nav.topViewController; controller.navigationItem.leftBarButtonItem=self.displayModeButtonItem; controller.navigationItem.leftBarButtonItem.title=((UINavigationController *)primaryViewController).topViewController.title; controller.navigationItem.leftItemsSupplementBackButton = true; } } }
UISplitViewController沒有給出獲取主和詳細視圖控制器的快捷屬性。在本程序中,viewControllers中的第一個就是主視圖控制器,第二個是詳細視圖控制器,並且與顯示模式無關。
另外,我發現在iOS 7的時代,有幾個其他委托也可以實現這種操作,但是在iOS 8裡已經過時了。經查閱文檔,官方推薦在該方法中處理。
5)在iPad豎屏時,初次進入App,詳細視圖左側沒有barButtonItem
這個問題可以通過初次初始化ImageViewController時設置leftBarButtonItem來解決:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. [self.scrollView addSubview:self.imageView]; [self startDownloadingImage]; if(self.traitCollection.userInterfaceIdiom==UIUserInterfaceIdiomPad) { self.navigationItem.leftBarButtonItem=self.splitViewController.displayModeButtonItem; }}
注意:這裡加了個判斷,只有在iPad裡才生效。如果不加判斷,iPhone裡初次顯示詳細視圖時會有問題。
之前很少用到UISplitViewController,發現有些東西剛開始還是有些不易理解。尤其結合著Size Class特性。遇到問題還是應該多看下官方文檔、WWDC相關視頻及Stackoverflow。代碼我已經上傳到Github,希望對你有幫助。
UISplitViewController和UISplitViewControllerDelegate
2014 WWDC視頻Building Adaptive Apps with UIKit
NShipster