web-dev-qa-db-ja.com

iPhoneの縦向きのUISplitViewControllerに、マスターではなく詳細VCが表示される

Xcode 6でUniversal Storyboardを使用して、iOS 7以降をターゲットにしています。 UISplitViewControllerを実装しました。これはiOS 8を実行しているiPhoneでネイティブにサポートされており、XcodeはiOS 7に自動的にバックポートします。マスタービューコントローラーが最初に表示されると予想したときに、ビューコントローラーが表示されます。 iOS 8でアプリを実行すると、マスタービューコントローラーが正しく表示されるため、これはiOS 8のバグであると考えられました。しかし、iOS 8は現在GMであり、これはまだ発生しています。 Split View Controllerが折りたたまれるとき(画面に表示されるView Controllerは1つだけ)、Split View Controllerが表示されるときに詳細ではなくマスターView Controllerが表示されるように設定するにはどうすればよいですか?

Interface BuilderでこのSplit View Controllerを作成しました。 Split View Controllerは、Tab Bar Controller内の最初のView Controllerです。マスターVCと詳細VCの両方が、Table View Controllerが内部に組み込まれたNavigation Controllerです。

171
Jordan H

ああ、これは数日間頭痛を引き起こしていたので、これをどうやってやるかわからなかった。最悪の部分は、マスター/ディテールテンプレートを使用して新しいXcode iOSプロジェクトを作成するとうまく機能したことです。幸いなことに、結局のところ、その小さな事実は、私が解決策を見つけた方法でした。

解決策は、UISplitViewControllerDelegateに新しいprimaryViewControllerForCollapsingSplitViewController:メソッドを実装することであることを示唆するいくつかの投稿があります。私はそれを役に立たなかった。動作するように見えるマスター詳細テンプレートでAppleが行うことは、新しいsplitViewController:collapseSecondaryViewController:ontoPrimaryViewController:デリゲートメソッドを実装することです(もう一度UISplitViewControllerDelegateに)。 docs によると、この方法:

プライマリView Controllerを調整し、セカンダリView Controllerを折りたたみインターフェイスに組み込むようデリゲートに要求します。

より具体的な詳細については、そのメソッドのディスカッション部分を必ず読んでください。

Appleがこれを処理する方法は次のとおりです。

- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
  ontoPrimaryViewController:(UIViewController *)primaryViewController {

    if ([secondaryViewController isKindOfClass:[UINavigationController class]]
        && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]]
        && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {

        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;

    } else {

        return NO;

    }
}

この実装は基本的に次のことを行います。

  1. secondaryViewControllerが期待しているもの(UINavigationController)であり、期待しているものを示している(DetailViewController-ビューコントローラー)が、モデルがない(detailItem)場合、 "Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  2. それ以外の場合は、「NO」を返して、Split View ControllerがセカンダリView Controllerのコンテンツを折りたたまれたインターフェイスに取り込めるようにします

結果は、ポートレートのiPhoneに対して次のようになります(ポートレートから開始するか、ポートレートに回転するか、より正確にコンパクトなサイズクラスになります)。

  1. ビューが正しい場合
    • モデルがあり、詳細ビューコントローラを表示します
    • モデルはありませんが、マスタービューコントローラを表示します
  2. ビューが正しくない場合
    • マスターView Controllerを表示します

泥だらけ。

231
Mark S

これがSwiftで受け入れられている答えです。このサブクラスを作成して、ストーリーボードのsplitViewControllerに割り当てるだけです。

//GlobalSplitViewController.Swift

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

  override func viewDidLoad() {
    super.viewDidLoad()

    self.delegate = self
  }

  func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController!, ontoPrimaryViewController primaryViewController: UIViewController!) -> Bool{
    return true
  }

}
58
Clifton Labrum

Mark Sの正解の迅速なバージョン

AppleのMaster-Detailテンプレートによって提供されます。

func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool {
    guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
    guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
    if topAsDetailController.detailItem == nil {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
    }
    return false
}

明確化

(Mark Sが言ったことは少し混乱を招きました)

このデリゲートメソッドは、splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:と呼ばれます。よりコンパクトな幅サイズに変更する場合(たとえば、携帯電話を横向きから縦向きに回転させる場合)、Split View Controllerをそれらの1つだけに折りたたむ必要があります。

この関数はブール値を返し、詳細を折りたたんでマスターを表示するかどうかを決定します。

したがって、この場合、詳細が選択されたかどうかに基づいて決定します。詳細が選択されているかどうかをどのように知ることができますか? Appleのマスター詳細テンプレートに従う場合、詳細ビューコントローラーには詳細情報を持つオプションの変数が必要です。そのため、nil(.None)の場合、まだ何も選択されていないため、ユーザーが何かを選択できるようにマスターを表示する必要があります.

それでおしまい。

19
NiñoScript

私のアプリはSwift 2.xで書かれており、うまく動作します。 (XCodeコンバーターを使用して)Swift 3.0に変換した後、ポートレートモードでマスターの代わりに詳細を最初に表示し始めます。問題は、splitViewController関数の名前がUISplitViewControllerDelegateの新しい名前に一致するように変更されていないことです。

その関数の名前を手動で変更した後、私のアプリは正しく動作するようになりました:

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
    guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
    guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
    if topAsDetailController.game == nil {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
    }
    return false
}
9
Tony

ドキュメント から、デリゲートを使用してUISplitViewControllernotに指示して、詳細ビューを「折りたたみインターフェイス」に組み込む必要があります」(つまり、あなたの場合の「ポートレートモード」)。 Swift 4では、そのために実装するデリゲートメソッドの名前が変更されました。

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
    return true
}
8
olito
   #import <UIKit/UIKit.h>

    @interface SplitProductView : UISplitViewController<UISplitViewControllerDelegate>




    @end

.m:

#import "SplitProductView.h"
#import "PriceDetailTableView.h"

@interface SplitProductView ()

@end

@implementation SplitProductView

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.delegate = self;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/
- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
  ontoPrimaryViewController:(UIViewController *)primaryViewController {

    if ([secondaryViewController isKindOfClass:[UINavigationController class]]
        && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[PriceDetailTableView class]]

        //&& ([(PriceDetailTableView *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)

        ) {

        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;

    } else {

        return NO;

    }
}
@end
8
Gank

詳細ビューコントローラーに表示するデフォルト値がない場合は、SplitViewControllerとストーリーボードの詳細UIViewControllerの間のデフォルトセグエを削除するだけです。これにより、常に最初にマスタービューコントローラーに入ります。

これの副作用は、ランドスケープで2つのビューを表示する代わりに、マスタービューコントローラーで詳細セグエを表示するまで、SplitViewControllerで1つのビューをフルサイズで表示することです。

7
Hao-Cher Hong

Cs193pの金曜日のセクションを見つけることができなかったすべての人々のために:

Swift 3.1.1では、UISplitViewControllerのサブクラスを作成し、そのデリゲートメソッドの1つを実装することは、私にとって魅力的でした。

class MainSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    self.delegate = self
}

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
    return true
} }

私の絵コンテ

3
Bartosz Kunat

私の意見では、この問題をより一般的に解決する必要があります。 UISplitViewControllerをサブクラス化し、埋め込みView Controllerにプロトコルを実装できます。

class MasterShowingSplitViewController: UISplitViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
}

extension MasterShowingSplitViewController: UISplitViewControllerDelegate {
    func splitViewController(splitViewController: UISplitViewController,
                             collapseSecondaryViewController secondaryViewController: UIViewController,
                             ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
        guard let masterNavigationController = primaryViewController as? UINavigationController,
                  master = masterNavigationController.topViewController as? SplitViewControllerCollapseProtocol else {
            return true
        }
        return master.shouldShowMasterOnCollapse()

    }
}

protocol SplitViewControllerCollapseProtocol {
    func shouldShowMasterOnCollapse() -> Bool
}

UITableViewControllerの実装例:

extension SettingsTableViewController: SplitViewControllerCollapseProtocol {
    func shouldShowMasterOnCollapse() -> Bool {
        return tableView.indexPathForSelectedRow == nil
    }
}

それが役に立てば幸い。したがって、このクラスを再利用でき、プロトコルを実装するだけで済みます。

3
Maik639

マスターから起動する必要がある場合は、SplitViewコントローラーからDetailViewControllerを削除するだけです。

UISplitViewController *splitViewController = (UISplitViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SETTINGS"];
splitViewController.delegate = self;
[self.navigationController presentViewController:splitViewController animated:YES completion:nil];
if (IPHONE) {
    NSMutableArray * cntlrs = [splitViewController.viewControllers mutableCopy];
    [cntlrs removeLastObject];
    splitViewController.viewControllers = cntlrs;
}
2

これはiOS-11およびSwift 4で機能しました:

//Following code in application didFinishLaunching (inside Application Delegate)
guard let splitViewController = window?.rootViewController as? UISplitViewController,
            let masterNavVC = splitViewController.viewControllers.first as? UINavigationController,
            let masterVC = masterNavVC.topViewController as? MasterViewController
else { fatalError() }
splitViewController.delegate = masterVC

//Following code in MasterViewController class
extension MasterViewController:UISplitViewControllerDelegate {
    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
        return true
    }
}
2
Vishal Chaudhry

この関数はSwiftの新しいバージョンで名前が変更されているため、このコードはSwift 4で機能します:

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }

    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
        return true
    }

}
1
Pink Panther

preferredDisplayModeUISplitViewControllerプロパティを.allVisibleに設定するだけです

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
0
Arash Etemad

Xamarin/C#ソリューション

public partial class MainSplitViewController : UISplitViewController
{
    public MainSplitViewController(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        Delegate = new MainSplitViewControllerDelegate();
    }
}

public class MainSplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController(UISplitViewController splitViewController, UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        return true;
    }
}
0
Mark Moeykens