打开 UISplitViewController 到 Master View 而不是 Detail

Open UISplitViewController to Master View rather than Detail

我有一个带有目标 iPhone 6 应用程序的拆分视图界面。在应用程序首次启动时,它会打开详细信息视图;我希望它能打开主视图。我试过:

self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryOverlay

在别处有人建议 但它似乎没有任何作用,并且在启动时不会打开主视图。我还尝试将以下行添加到我的 AppDelegate:

splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:

但是尽管返回 true 或 false (Another Prior Stack Overflow Question),但我没有成功。

我确实在 Xcode 中启动了示例 Master-Detail 应用程序,它加载到基于 splitViewController 的主视图:调用返回 false;但是,我不确定如何在更复杂的布局中进行这项工作。

Swift

不是要显示主视图,而是要在主视图下方全宽显示详细信息视图。

UISplitViewController in portrait on iPhone shows detail VC instead of master讲的是崩溃机制的原理

本回答地址:

  • 大师→细节 (压缩宽度)
    • iPhone 4s, 5, 5s, SE, 6, 6s, 7(任意方向)
    • iPod Touch
    • 任意 iPhone 加(纵向)
  • 并排 (所有其他尺寸)
    • iPad
    • 任意 iPhone 加(横向)

您必须设置preferredDisplayMode。你想要的是 .primaryVisible 如果它存在的话!如果只有 1 个视图适合 (紧凑宽度),则使用 .allVisible、iOS 会选择 Detail;在那个尺寸下,下面的代码将选择 Master.

诀窍是将preferredDisplayMode改为.allVisible改为return truecollapseSecondary:onto.

class PrimarySplitViewController: UISplitViewController,
                                  UISplitViewControllerDelegate {

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

    func splitViewController(
             _ splitViewController: UISplitViewController,
             collapseSecondary secondaryViewController: UIViewController,
             onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true 
    }
}

On the first launch of the application, it opens to the Detail View; I would like it to open to the Master View

假设您只希望在第一次启动时这样做,但并非总是如此;例如,在主视图显示空数据集的情况下;那么解决方案就像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
}

步骤 1 - 打开 MasterViewController

步骤 2 - 确保 table 视图具有 UISplitViewControllerDelegate 协议。例如:

class ListVC: UITableViewController,UISplitViewControllerDelegate {}

步骤 3 - 添加到 ViewDidLoad

splitViewController?.delegate = self

第 4 步 - 然后重写此方法以说明主视图控制器应始终折叠到详细视图控制器上:

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

这是一个古老的问题,none 的答案是针对 Objective C 的,即使我移植了 Swift 的答案,none 也对我有用。一个很接近,@SwiftArchitect.

但他建议将内容模式设置为 .allVisible(Objective C 中的 UISplitViewControllerDisplayModeAllVisible)- 这使得主视图始终显示,将视图拆分为一侧的主视图, 细节在另一个。这有点酷,但是 OP 特别要求在初始启动时显示主视图,这是我需要做的。

更改是使用 UISplitViewControllerDisplayModePrimaryOverlay 作为显示模式。

此答案适用于 Xcode 9.4.1,部署目标 11.4。

这里是 MasterViewController.h - 你需要在协议声明中添加 UISplitViewControllerDelegate:

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "MasterDetailDemo+CoreDataModel.h"

@class DetailViewController;

@interface MasterViewController : UITableViewController
<UISplitViewControllerDelegate,
NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) DetailViewController *detailViewController;

@property (strong, nonatomic) NSFetchedResultsController<Event *> *fetchedResultsController;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;

@end

然后在您的 MasterViewController.m 中,您需要在 ViewDidLoad 中设置拆分视图控制器委托和内容模式,并按照@SwiftArchitect 的回答添加拆分视图控制器委托方法:

- (void)viewDidLoad {

    [super viewDidLoad];

    // needed to "slide out" MasterView on startup on iPad
    self.splitViewController.delegate = self;
    self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;

    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];

    self.navigationItem.rightBarButtonItem = addButton;

    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}

// split view delegate method
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    return true;
}

注意:经过一些测试,我发现拆分视图委托方法和拆分视图协议不是必需的。没有它,它似乎工作完全一样。也许这是 iOS 的变化的结果,因为最初提出并回答了这个问题。

只要将这一行放在我的 ViewDidLoad 方法中,我就可以正常工作:

self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;

或者只是继承自 UISplitViewController 并在故事板中使用这个新的 class(基于 SwiftArchitect 的回答):

class MasterShowingSplitViewController :UISplitViewController, UISplitViewControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        self.preferredDisplayMode = .allVisible
    }

    func splitViewController(
        _ splitViewController: UISplitViewController,
        collapseSecondary secondaryViewController: UIViewController,
        onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true
    }
}

Swift 5, iOS 13

我发现其他答案很有用,但不完全是因为它们在 iPad 或 iPhone 上产生了我想要的行为,但不是在两者上都产生了我想要的行为。

下面的解决方案是我使用的:

iPhone: 主视图总是最先出现

iPad人像:细节总是出现,但有主人覆盖它;细节是全屏的(不仅仅是主人的权利)

iPad风景:主图总是在左边,细节总是在右边

class RootSplitViewController: UISplitViewController {
    override func viewDidLoad() {
        if UIDevice.current.userInterfaceIdiom == .pad {
            self.preferredDisplayMode = .automatic
        }
        else {
            self.preferredDisplayMode = .allVisible
        }
        self.delegate = self
    }
}

extension RootSplitViewController: UISplitViewControllerDelegate {
    func splitViewController(_ splitViewController: UISplitViewController,
                             collapseSecondary secondaryViewController:UIViewController,
                             onto primaryViewController:UIViewController)
        -> Bool
    {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
        // Or: return false if your application logic makes this appropriate
        // return false
    }
}

iOS14

我没有收到 splitViewController(_:collapseSecondary:onto:) 的回调,而是使用了以下新方法。

func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
    return .primary
}

iOS 14

WWDC 2020 - Build for iPad 开始,您可以为紧凑宽度 class 添加特定的视图控制器(例如 iPhone 纵向,iPad 侧拉),方法是选中 在 SplitViewController 的属性检查器中使用单独的视图控制器

因此您可以通过设置关系 segue 将任何视图控制器设置为您想要的初始视图控制器。

iOS 14 -- 两列模式更新

在最终发现拆分视图控制器已在 iOS14 中重新设计之前,我为此苦苦挣扎了一段时间,因此上面的 none 答案不再相关。

我建议从这篇文章开始 here

但如果您正在寻找快速修复:

  • 您需要在拆分视图控制器上设置“紧凑视图控制器”关系。您可以通过右键单击 Split View Controller 并将新关系拖动到您想要以紧凑模式显示的视图控制器来执行此操作。
  • 我的应用程序有一个 TableView,在紧凑模式下,我想在点击一个单元格时推送一个 Detail View Controller。在新的 iOS 14 SplitView Controller 中,这必须手动完成。我通过将以下内容添加到我的 didSelectRowAt 函数来做到这一点:
        // If we are in compact mode, we need to push the detail view controller
        if let splitViewController = splitViewController {
            if splitViewController.isCollapsed {
                let shipmentDetailViewController = storyboard?.instantiateViewController(identifier: "shipmentDetailViewController") as! ShipmentDetailViewController
                shipmentDetailViewController.shipment = selectedShipment
                self.navigationController?.pushViewController(shipmentDetailViewController, animated: true)
            }
        }