2013年5月20日 星期一

UIContainerViewController

@interface UIViewController (UIContainerViewControllerProtectedMethods)
@property(nonatomic,readonly) NSArray *childViewControllers NS_AVAILABLE_IOS(5_0);
- (void)addChildViewController:(UIViewController *)childController NS_AVAILABLE_IOS(5_0);
- (void)removeFromParentViewController NS_AVAILABLE_IOS(5_0);
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(5_0);

@interface UIViewController (UIContainerViewControllerCallbacks)
- (void)willMoveToParentViewController:(UIViewController *)parent NS_AVAILABLE_IOS(5_0);
- (void)didMoveToParentViewController:(UIViewController *)parent NS_AVAILABLE_IOS(5_0);
從官方UIViewController.h節錄下來, 目前常用到的幾個方法以及一個childArray


先建立一個Single View Application, Product Name為Container, Devices為iPhone
勾選Use Storyboard以及Use Automatic Reference Counting

專案建立好之後, 先建三個Objective-C Class
分別命名為ContainerViewController0, ContainerViewController1, ContainerViewController2
不要勾選Targeted for iPad 以及 不要勾選 With XIB for user interface
接著三個Class都新增以下方法
- (void)willMoveToParentViewController:(UIViewController *)parent {
 [super willMoveToParentViewController:parent];
}

- (void)didMoveToParentViewController:(UIViewController *)parent {
 [super didMoveToParentViewController:parent];
}
這兩個Method可以想像成viewWillAppear以及viewDidAppear, 做為Callbacks用
其中一個Class完成如下圖, 我有多寫一些辨識用的Log

接著去StoryBoard然後拉幾個Objects出來
三個Button分別設Tag為1,2,3
新增一個Container, 而Container會自帶一個View Controller, View Controller大小會隨著Container大小改變, 自己拉至想要的大小
接著在額外拉兩個View Controller出來
將View Controller的Size設為Freefrom, Status Bar設為None, Resize View From NIB取消勾選
然後把View大小設成跟Container大小一樣
設定一些背景色跟拉個Label作為辨識用


然後將三個View Controller, 分別對應至三個ContainerViewController Class, 並且設定Storyboard ID


然後在ViewController.m新增一個IBAction, 內容可以先寫個NSLog(@"%d", [sender tag]);
然後將Storyboard中ViewController的三個Button都連線至同一個IBAction

到這邊可以先Compile & Run 一次, 會出現以下內容

Console會顯示
Container[17255:11303] ContainerViewController0 initWithCoder
Container[17255:11303] ContainerViewController0 viewDidLoad
Container[17255:11303] ContainerViewController0 willMoveToParentViewController
Container[17255:11303] ContainerViewController0 didMoveToParentViewController

而按下Button會顯示對應的Button Tag(會Log出 0 or 1 or 2)
若是Button有問題表示連線沒連好


接著回到程式的IBAction, 將 NSLog(@"%d", [sender tag]); 刪掉改為
UIViewController *from = self.childViewControllers[0];
 [from willMoveToParentViewController:nil]; //willMoveToParentViewController
 
 UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
 NSString *viewControllerID = [NSString stringWithFormat:@"ContainerViewController%d", [sender tag]];
 UIViewController *to = [storyboard instantiateViewControllerWithIdentifier:viewControllerID];
 [self addChildViewController:to];   //willMoveToParentViewController
 
 [self transitionFromViewController:from
       toViewController:to
         duration:1.0
          options:UIViewAnimationOptionTransitionFlipFromLeft
       animations:^{
       }
       completion:^(BOOL finished) {
        [from removeFromParentViewController];  //didMoveToParentViewController
        [to didMoveToParentViewController:self]; //didMoveToParentViewController
       }];

willMoveToParentViewController 及 didMoveToParentViewController
這部分我可能理解有錯, 參考一下就好

willMoveToParentViewController 除了直接呼叫之外
在 addChildViewController會自動呼叫

didMoveToParentViewController 除了直接呼叫之外
在 removeFromParentViewController會自動呼叫

我IBAction內容是寫法是保持childViewControllers始終只有一個viewController
所以每次只要拿index最上層就好, 並且當作transition的來源from
而to的來源則為每次都從storyboard的id去取得, 取得之後再用instantiateViewControllerWithIdentifier實體化, 然後加到childViewControllers(這時候childViewControllers就會有兩個內容, from及to兩個)
然後就開始執行 transitionFromViewController
而在轉換完成之後的completion blocks
我將原本的from從childViewControllers移除, 使得childViewControllers內容又變回一個

註解寫的即為會呼叫 willMoveToParentViewController 及 didMoveToParentViewController 的時機

完整的Example

沒有留言:

張貼留言