UISplitViewController在iPhone上的肖像显示详细VC而不是主

时间:2014-09-16 18:04:30

标签: ios interface-builder ios8 uisplitviewcontroller master-detail

我正在使用Xcode 6中的通用故事板,目标是iOS 7及更高版本。我已经实现了一个UISplitViewController,现在支持运行iOS 8的iPhone,而Xcode会自动向iOS 7支持它。它运行得非常好,除非你在iPhone上以iOS 8的肖像形式启动应用程序,当我希望第一次看到主视图控制器时,会显示拆分视图的详细视图控制器。我相信这是iOS 8的一个错误,因为当您在iOS 7上运行应用程序时,它会正确显示主视图控制器。但iOS 8现在是通用汽车,而且这种情况仍在发生。如何进行设置以便在拆分拆分视图控制器时(屏幕上只显示一个视图控制器),当显示拆分视图控制器时,它会显示主视图控制器而不是详细信息?

我在Interface Builder中创建了这个拆分视图控制器。拆分视图控制器是标签栏控制器中的第一个视图控制器。主控和详细VC都是导航控制器,其中嵌入了表视图控制器。

14 个答案:

答案 0 :(得分:230)

哦,伙计,这让我头疼了几天,无法弄清楚如何做到这一点。最糟糕的是,使用master-detail模板创建一个新的Xcode iOS项目工作得很好。幸运的是,最后,我找到解决方案的那个小事实。

我发现有些帖子表明解决方案是在primaryViewControllerForCollapsingSplitViewController:上实施新的UISplitViewControllerDelegate方法。我试过没有用。 Apple在主要细节模板中的作用似乎是实现新的(深呼吸说出所有这一切)splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:委托方法(再次在UISplitViewControllerDelegate上)。根据{{​​3}},这种方法:

  

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

请务必阅读该方法的讨论部分以获取更具体的细节。

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让分割视图控制器尝试将辅助视图控制器的内容合并到折叠的界面中”
  3. iPhone的纵向结果如下(从纵向开始或以纵向旋转 - 或者更准确地说是紧凑的尺寸等级):

    1. 如果您的观点是正确的
      • 并有一个模型,显示详细视图控制器
      • 但没有型号,显示主视图控制器
    2. 如果您的观点不正确
      • 显示主视图控制器
    3. 清除泥浆。

答案 1 :(得分:58)

以下是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
  }

}

答案 2 :(得分:19)

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:,因为它的作用是什么。当更改为更紧凑的宽度尺寸时(例如,将手机从横向旋转到纵向时),需要将分割视图控制器折叠为其中一个。

此函数返回一个布尔值,以决定是否应折叠详细信息并显示主文件。

因此,在我们的案例中,我们将根据是否选择了详细信息来决定。我们如何知道我们的细节是否被选中?如果我们遵循Apple的Master-Detail模板,详细视图控制器应该有一个带有详细信息的可选变量,所以如果它是nil(.None),那么还没有选择我们应该显示主人,以便用户可以选择一些东西。

那就是它。

答案 3 :(得分: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
}

答案 4 :(得分:8)

   #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

答案 5 :(得分:8)

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

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

答案 6 :(得分:7)

如果您没有要在详细视图控制器中显示的默认值,您只需删除故事板中SplitViewController和详细信息UIViewController之间的默认segue。 这将使它始终首先进入主视图控制器。

这样做的副作用是,在横向视图中看到两个视图,您将在SplitViewController中看到一个完整大小的视图,直到主视图控制器中的Show Detail Segue被触发。

答案 7 :(得分: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
    }
}

希望它有所帮助。 所以你可以重用这个类,只需要实现一个协议。

答案 8 :(得分:3)

对于那些无法找到cs193p星期五版块的人:

在Swift 3.1.1中创建UISplitViewController的子类并实现其代理方法之一就像魅力一样为我工作:

glob.glob('*.txt')

My storyboard

答案 9 :(得分: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;
}

答案 10 :(得分: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
    }
}

答案 11 :(得分:1)

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

}

答案 12 :(得分:0)

Xamarin / C#Solution

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

答案 13 :(得分:0)

只需将preferredDisplayMode的{​​{1}}属性设置为UISplitViewController

.allVisible