UISplitViewController 在 iPhone 上的肖像显示了 VC 而不是 master 的详细信息

我在 Xcode 6中使用通用故事板,目标是 iOS 7及以上版本。我已经实现了一个 UISplitViewController,它现在在运行 iOS8的 iPhone 上得到了本地支持,Xcode 将自动为 iOS7备份它。它工作得非常好,除了当你在 iPhone 上以纵向运行 iOS8的方式启动应用程序时,当我期望第一次看到主视图控制器时,分割视图的详细视图控制器就会显示出来。我认为这是 iOS8的一个 bug,因为当您在 iOS7上运行应用程序时,它会正确地显示主视图控制器。但 iOS8现在是通用汽车,而且这种情况仍在发生。如何设置它,以便当分割视图控制器将要折叠(只有一个视图控制器显示在屏幕上) ,当分割视图控制器显示它显示主视图控制器而不是细节?

我用 Interface Builder 创建了这个分割视图控制器。分割视图控制器是选项卡条控制器中的第一个视图控制器。主 VC 和细节 VC 都是导航控制器,其中嵌入了表视图控制器。

63664 次浏览

哦,天哪,这让我头疼了好几天,我不知道该怎么做。最糟糕的部分是,创建一个新的 Xcode iOS 项目与主-详细信息模板工作得很好。幸运的是,最终,这个小小的事实让我找到了解决办法。

我发现有一些帖子建议解决方案是在 UISplitViewControllerDelegate上实现新的 primaryViewControllerForCollapsingSplitViewController:方法。我试过了,但没用。苹果在主-细节模板中所做的似乎是实现新的 splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:委托方法(同样是在 UISplitViewControllerDelegate上)。根据 医生,这种方法:

请求委托调整主视图控制器并将辅助视图控制器合并到折叠接口中。

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

苹果处理这个问题的方式是:

- (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,让分割视图控制器尝试将辅助视图控制器的内容合并到折叠接口中”

结果如下: iPhone 的纵向尺寸(从纵向开始或者旋转到纵向——或者更准确地说是紧凑尺寸类) :

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

清清楚楚。

   #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

如果没有显示详细视图控制器的默认值,那么只需删除故事板中 SplitViewController 和详细 UIViewController 之间的默认接口即可。 这将使它总是首先进入主视图控制器。

这样做的副作用是,在 SplitViewController 中不会看到横向的两个视图,而是看到一个全尺寸的视图,直到主视图控制器中的 Show Details Segue 激活。

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


}

马克 · S 正确答案的快速版本

由苹果的 Master-Details 模板提供。

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
}

澄清一下

(马克 · S 说的话有点令人困惑)

这个委托方法称为 splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:,因为它就是这么做的。当改变到一个更小的宽度大小(例如,当旋转手机从横向到纵向) ,它需要折叠分割视图控制器只成为其中之一。

这个函数返回一个布尔值来决定它是否应该折叠 Detail 并显示 Master。

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

就是这样。

在我看来,你应该解决这个问题更一般。您可以子类化 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
}
}

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

我的应用程序是用 Swift 2.x 编写的,运行良好。在将其转换成 Swift 3.0(使用 XCode 转换器)之后,它开始首先显示细节,而不是在纵向模式下显示 master。问题是没有更改 splitViewController 函数的名称以匹配 UISplitViewControllerGenerate 的新名称。

手动更改该功能的名称后,我的应用程序现在可以正常工作了:

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
}

当需要从 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;
}

文件开始,您需要使用一个委托来告诉 UISplitViewController 没有将细节视图合并到“折叠接口”中(在您的例子中是“ Portrait 模式”)。在 Swift 4中,为其实现的委托方法已被重命名为:

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

对于所有找不到 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
} }

我的故事板

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

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

这个函数在新版本的 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
}


}

只需将 UISplitViewControllerpreferredDisplayMode属性设置为 .allVisible

class MySplitViewController: UISplitViewController {


override func viewDidLoad() {
super.viewDidLoad()


self.preferredDisplayMode = .allVisible
}


}