LoginSignup
87
84

More than 5 years have passed since last update.

iOS8から変更になったUISplitViewControllerについて調べてみた

Posted at

iOS8からUISplitViewControllerが大きく変更になり、
iPadだけでなく、iPhoneでも使えるようになりました。

その新しいUISplitViewControllerについて分かった範囲でまとめてみました。

リファレンスで出てくる単語について

状態を表す単語

expanded

  • UISplitViewControllerが2つのViewControllerを持ち、分割表示が可能な状態
  • horizontal size class が"compact"の時、この状態となる

collapsed

  • UISplitViewControllerが1つのViewControllerしか持っておらず、分割表示ができない状態
  • horizotal size class が"regular"の時、この状態となる

Split画面を表す単語

primary

  • 分割表示時の左側
  • UISplitViewControllerのviewCotnrollersの0番目

secondary

  • 分割表示時の右側
  • UISplitViewControllerのviewCotnrollersの1番目

表示判定は、iPhone/iPadではなくsize classを見て行われる

横幅のsize classがRegularの場合に分割表示となり、
横幅のsize classがCompactの場合に1画面表示となる

Expaded状態のときの表示モード指定

  • Automatic (デフォルト)

    環境に応じて自動的に切り替わる。
    例えば、iPadなら縦向きの場合、PrimaryOverlay。横向きの場合、PrimaryHiddenとなる。

  • PrimaryHidden

    primary画面を隠し、secondary画面だけを表示する。

  • AllVisible

    primary画面とsecondary画面の両方を隠すことなく表示する

  • PrimaryOverlay

    seconary画面に覆いかぶさる形でprimary画面が表示される

参考にするサンプル

AppleのサンプルコードAdaptivePhotos

ポイント

サンプルの中で、UISplitViewControllerを扱う上で重要そうなところについて調べてみました。

1. UISplitViewControllerの生成

ここでは、Expanded時の表示モードの指定も行っています

AAPLAppDelegate.m
UISplitViewController *controller = [[UISplitViewController alloc] init];

controller.delegate = self;

AAPLListTableViewController *master = [[AAPLListTableViewController alloc] init];

master.user = user;
UINavigationController *masterNav = [[UINavigationController alloc] initWithRootViewController:master];

AAPLEmptyViewController *detail = [[AAPLEmptyViewController alloc] init];

controller.viewControllers = @[masterNav, detail];
// Expanded時の表示モードを指定
controller.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
self.window.rootViewController = controller;

2. UISplitViewControllerDelegateの実装

Collapsed状態になるときに、primary画面とsecondary画面のどちらを表示するか指定する
primary画面を表示する場合、YES
secondary画面を表示する場合、NO
NOの場合、primary側がUINavigationControllerであれば、
viewControllersの1番目のViewControllerが表示される。
そのため、ここでは、表示したいViewControllerを
UINavigationControllerのviewControllersにセットしている。
ちなみに、primary側がただのViewController場合、以下のメソッドが実行される。

collapseSecondaryViewController:forSplitViewController:splitViewController

AAPLAppDelegate.m
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController
{
    AAPLPhoto *photo = [secondaryViewController aapl_containedPhoto];
    if (!photo) {
        // If our secondary controller doesn't show a photo, do the collapse ourself by doing nothing.
        return YES;
    }

    // Before collapsing, remove any view controllers on our stack that don't match the photo we are about to merge on.
    if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
        NSMutableArray *viewControllers = [NSMutableArray array];
        for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
            if ([controller aapl_containsPhoto:photo]) {
                [viewControllers addObject:controller];
            }
        }
        [(UINavigationController *)primaryViewController setViewControllers:viewControllers];
    }
    return NO;
}

Expanded状態になるときに、secondary側に表示するViewControllerを指定する。
nilを返す場合、primary側がUINavigationControllerであれば、
そのviewControllersの0番目がprimary側、1番目がsecondary側に表示される。
ちなみに、primary側がただのViewControllerの場合、以下のメソッドが実行される。

separateSecondaryViewControllerForSplitViewController:

AAPLAppDelegate.m
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController
{
    if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
        NSArray *viewControllers = [(UINavigationController *)primaryViewController viewControllers];
        for (UIViewController *controller in viewControllers) {
            if ([controller aapl_containedPhoto]) {
                // Do the standard behavior if we have a photo.
                return nil;
            }
        }
    }
    // If there's no content on the navigation stack, make an empty view controller for the detail side.
    return [[AAPLEmptyViewController alloc] init];
}

3. primary側のViewController実装 (AAPLListTableViewController)

ここでは、写真の枚数に応じて、行の表示や選択時の処理を変えている

仕様

  • 1枚

    Collapsed時は、セルの右端に「>」を表示し、
    Expanded時は、セル選択時に右に写真詳細を表示

  • 2枚以上

    Collapsed時でも、Expanded時でも、セルの右端に「>」を表示し、
    セル選択時にはPushでAAPLConversationViewControllerを表示する

ここでは、セルのaccessoryTypeを表示を指定している
iOS8から追加された、targetViewControllerForActionが使うことで、
push遷移が可能かどうか判定し、push可能ならaccessoryTypeを表示している

AAPLListTableViewController.m
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Whether to show the disclosure indicator for this cell.
    BOOL pushes;

    if([self shouldShowConversationViewForIndexPath:indexPath]) {
        // If the conversation corresponding to this row has multiple photos.
        pushes = [self aapl_willShowingViewControllerPushWithSender:self];
    } else {
        // If the conversation corresponding to this row has a single photo.
        pushes = [self aapl_willShowingDetailViewControllerPushWithSender:self];
    }

    // Only show a disclosure indicator if selecting this cell will trigger a push in the master view controller (the navigation controller above ourself).
    if(pushes) {
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    AAPLConversation *conversation = [self conversationForIndexPath:indexPath];

    cell.textLabel.text = conversation.name;
}

ここでは、セル選択時の表示方法を指定している
写真が複数枚の場合、showViewController実行
これは、UINavigationControllerのpushと同じ動きをする
写真が1枚の場合、showDetailViewController実行
これは、UISplitViewControllerのshowDetailViewControllerの動きをする

  • horizotal size classがregularの場合(分割表示)

    secondary側が置き換わる

  • horizotal size classがcompactの場合(1画面表示)

    pushで画面遷移する

AAPLListTableViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    AAPLConversation *conversation = [self conversationForIndexPath:indexPath];

    if([self shouldShowConversationViewForIndexPath:indexPath]) {
        AAPLConversationViewController *controller = [[AAPLConversationViewController alloc] init];

        controller.conversation = conversation;
        controller.title = conversation.name;

        // If this row has a conversation, we just want to show it.
        [self showViewController:controller sender:self];
    } else {
        AAPLPhoto *photo = [conversation.photos lastObject];
        AAPLPhotoViewController *controller = [[AAPLPhotoViewController alloc] init];

        controller.photo = photo;
        controller.title = conversation.name;

        // If this row has a single photo, then show it as the detail (if possible).
        [self showDetailViewController:controller sender:self];
    }
}

以上です。

87
84
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
87
84