iPhone上的肖像中的UISplitViewController显示详细的VC而不是master


177

我正在Xcode 6中使用Universal Storyboard,目标是iOS 7及更高版本。我已经实现了一个UISplitViewController,现在在运行iOS 8的iPhone上本机支持,并且Xcode会自动将其移植到iOS7。它运行得非常好,除了当您在运行iOS 8的人像模式下在iPhone上启动该应用程序时,拆分视图的详细视图当我希望第一次看到主视图控制器时,将显示controller。我认为这是iOS 8的错误,因为当您在iOS 7上运行应用程序时,它会正确显示主视图控制器。但是iOS 8现在是通用汽车,而且这种情况仍在发生。我如何设置它,以便在拆分视图控制器要折叠时(屏幕上仅显示一个视图控制器),在显示拆分视图控制器时它显示主视图控制器而不显示详细信息?

我已经在Interface Builder中创建了此拆分视图控制器。拆分视图控制器是选项卡栏控制器中的第一个视图控制器。主VC和详细VC都是导航控制器,其中嵌入了表格视图控制器。

Answers:


238

哦,老兄,这让我头疼了好几天,无法弄清楚该怎么做。最糟糕的部分是使用master-detail模板创建一个新的Xcode iOS项目就可以了。幸运的是,最后,几乎没有什么是我找到解决方案的方法。

我发现有一些帖子建议解决方案是在上实现新primaryViewControllerForCollapsingSplitViewController:方法UISplitViewControllerDelegate。我尝试了一下都无济于事。Apple在似乎有效的主从模板中所做的是实现新的(深呼吸地说所有这一切)splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:委托方法(再次UISplitViewControllerDelegate)。根据docs,此方法:

要求代表调整主视图控制器,并将辅助视图控制器合并到折叠的界面中。

确保阅读该方法的讨论部分以获取更多具体细节。

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是我们期望的(a UINavigationController),并且正在显示我们期望的(a- DetailViewController您的视图控制器),但没有模型(detailItem),则Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
  2. 否则,返回“ NO让拆分视图控制器尝试将辅助视图控制器的内容合并到折叠的界面中”

对于iPhone(纵向)(以纵向开始或旋转为纵向-或更准确地说是紧凑尺寸类),结果如下:

  1. 如果你的观点是正确的
    • 并有一个模型,显示细节视图控制器
    • 但没有模型,请显示主视图控制器
  2. 如果你的观点不正确
    • 显示主视图控制器

清澈如泥。


8
很棒的答案!我只是简单地继承了子类,UISplitViewController并始终YES从该方法返回,然后只更改了Storyboard中的拆分视图类,因为我一直希望以肖像的方式在iPhone上显示母版。:)
约旦H

2
如果“ iPhone”处于“人像”模式,我希望隐藏主视图控制器,因为我有默认的详细视图控制器设置。我怎样才能做到这一点。我的主要资料和详细资料均为VC类型。具体来说,我的细节是MMDrawerController。请帮助
Harshit Gupta 2014年

3
我尝试了乔伊关于子类化的建议,UISplitViewController但是发现那是行不通的:splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:从未调用过。相反,我复制了Apple的模板并将其放在AppDelagate中。这也需要对在下面创建UISplitViewController进行一些更改application didFinishLaunchingWithOptions:(我也复制了Apple的模板)。
尼克

7
@joey的注释可以通过设置self.delegate = self; 在viewdidload中!并在.h中添加<UISplitViewControllerDelegate>谢谢!
worldworldcitizen 2014年

2
这对我来说似乎是正确的答案,因为我遇到了完全相同的问题。但是,由于某种原因,我splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:从未得到过电话。似乎该委托正在正确设置我的应用程序委托的applicationDidFinishLaunchingWithOptions:方法。有没有其他人看到过这个问题并且没有解决方案?
蒂姆·迪恩

60

这是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
  }

}

3
很好,这很有帮助。但是出现了一个新问题。现在,将我带到主菜单的后退按钮消失了(从不显示)。我如何找回它?编辑:没关系,想通自己:-)。对于其他用户:将其添加到detailView中:self.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()self.navigationItem.leftItemsSupplementBackButton = true
Tom Tallak Solbu

3
现在在Swift中无论如何func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
Dan Rosenstark

2
似乎仅在类大小紧凑时才调用委托方法。它在iPhone上被调用,但在iPad纵向上没有被调用,这意味着它不能解决问题,因为iPad纵向也处于折叠模式。经过iOS 12.1测试
丹尼尔(Daniel)

21

迅捷版Mark S的正确答案

如Apple的主从模板所提供。

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:,因为它就是这样做的。当更改为更紧凑的宽度大小时(例如,将手机从横向旋转到纵向时),它需要将拆分视图控制器折叠为其中一个。

此函数返回一个布尔值,以决定是否应折叠Detail并显示Master。

因此,在本例中,我们将根据是否选择了详细信息来决定。我们如何知道是否选择了我们的详细信息?如果我们遵循Apple的Master-Detail模板,则详细视图控制器应具有一个包含详细信息的可选变量,因此如果它为nil(.None),则没有任何选择,我们应该显示Master以便用户可以选择某些内容。

而已。


只是为了阐明为什么我从@sschale的编辑回滚。该代码是的引号Apple's Master-Detail template,并非意在精简,仅是事实。:)
NiñoScript

10

文档中,您需要使用委托来告诉代理UISplitViewController 不要将详细信息视图合并到“折叠的界面”(即您的情况下的“纵向模式”)中。在Swift 4中,要实现的委托方法已重命名:

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

9
   #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

9

我的应用程序是用Swift 2.x编写的,可以正常运行。将其转换为Swift 3.0(使用XCode转换器)后,它开始首先显示细节,而不是纵向显示主图像。问题是未更改函数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
}

我和您有同样的问题,但我不理解您的解决方案。您在此处发布的代码没有任何变化。你可以再详细一点吗。谢谢
bibscy 16/12/12

许多方法都被重命名。
戴夫

Tony的答案是@NiñoScript的答案的Swift 3语法(为以前的Swift版本编写)
Hellojeffy

2
快速3,别忘了self.delegate = self使用viewDidLoad方法。
费尔

7

如果没有默认值显示在详细视图控制器中,则只需在故事板上删除SplitViewController和详细信息UIViewController之间的默认设置即可。这将使其始终先进入Master View Controller。

这样做的副作用是,您不会在横向视图中看到两个视图,而是会在SplitViewController中看到一个完整尺寸的视图,直到主视图控制器中的Show Detail Segue被触发为止。


好招。我的应用仅处于纵向模式,我可以做到这一点。
和平

这是正确的,除了在“横向”方向上,您会看到视图的右侧空白部分可能是灰色填充。
vedrano

4

对于所有找不到cs193p星期五部分的人:

在Swift 3.1.1中,创建UISplitViewController的子类并实现其委托方法之一对我来说就像一个魅力:

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
} }

我的故事板


正如@olito指出的那样,在Swift 4中,其语法已更改为: public func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool
Robuske

3

我认为您应该更通用地解决此问题。您可以子类化UISplitViewController并在嵌入式视图控制器中实现协议。

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
    }
}

希望能帮助到你。因此,您可以重用此类,只需要实现一个协议即可。


委托方法永远不会被调用!
K_Mohit

在iPad和iPhone 6/7/8 Plus上没有调用它。那是你的问题吗?看看:stackoverflow.com/questions/29767614/…–
Maik639

2

只要需要从Master启动,请从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

该功能在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
    }

}

0

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

只需将的preferredDisplayMode属性设置UISplitViewController.allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.