ios iPhone 上的纵向 UISplitViewController 显示细节 VC 而不是主
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25875618/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UISplitViewController in portrait on iPhone shows detail VC instead of master
提问by Jordan H
I am using a Universal Storyboard in Xcode 6, targeting iOS 7 and above. I've implemented a UISplitViewController
which is now natively supported on iPhone running iOS 8, and Xcode will automatically backport it for iOS 7. It's working really well, except when you launch the app on iPhone in portrait running iOS 8, the split view's detail view controller is displayed when I expected to first see the master view controller. I believed this was a bug with iOS 8 because when you run the app on iOS 7, it correctly shows the master view controller. But iOS 8 is now GM and this is still occurring. How can I set it up so that when the split view controller is going to be collapsed (only one view controller displayed on screen), when the split view controller is displayed it shows the master view controller not the detail?
我在 Xcode 6 中使用通用故事板,针对 iOS 7 及更高版本。我已经实现了一个UISplitViewController
现在在运行 iOS 8 的 iPhone 上本机支持的,并且 Xcode 将自动将它向后移植到 iOS 7。它运行得非常好,除非你在 iPhone 上以纵向运行 iOS 8 的方式启动应用程序时,拆分视图的详细信息视图当我希望第一次看到主视图控制器时显示控制器。我认为这是 iOS 8 的一个错误,因为当您在 iOS 7 上运行该应用程序时,它会正确显示主视图控制器。但是 iOS 8 现在是 GM,而且这种情况仍在发生。我如何设置它以便当拆分视图控制器将要折叠时(屏幕上只显示一个视图控制器),当显示拆分视图控制器时,它显示主视图控制器而不是细节?
I've created this split view controller in Interface Builder. The split view controller is the first view controller within a tab bar controller. Both the master and the detail VCs are navigation controllers with table view controllers embedded inside.
我在 Interface Builder 中创建了这个拆分视图控制器。拆分视图控制器是标签栏控制器中的第一个视图控制器。master 和 detail VC 都是导航控制器,其中嵌入了表视图控制器。
回答by Mark S
Oh man, this was causing me a headache for a few days and could not figure out how to do this. The worst part was that creating a new Xcode iOS project with the master-detail template worked just fine. Fortunately, in the end, that little fact was how I found the solution.
哦,伙计,这让我头痛了几天,无法弄清楚如何做到这一点。最糟糕的是,使用主从模板创建一个新的 Xcode iOS 项目效果很好。幸运的是,最后,这个小事实是我找到解决方案的方式。
There are some posts I've found that suggest that the solution is to implement the new primaryViewControllerForCollapsingSplitViewController:
method on UISplitViewControllerDelegate
. I tried that to no avail. What Apple does in the master-detail template that seems to work is implement the new (take a deep breath to say all of this one) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
delegate method (again on UISplitViewControllerDelegate
). According to the docs, this method:
我发现有一些帖子表明解决方案是primaryViewControllerForCollapsingSplitViewController:
在UISplitViewControllerDelegate
. 我试过没有用。Apple 在似乎有效的 master-detail 模板中所做的是实现新的(深呼吸说所有这一切)splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
委托方法(再次在UISplitViewControllerDelegate
)。根据文档,此方法:
Asks the delegate to adjust the primary view controller and to incorporate the secondary view controller into the collapsed interface.
要求代理调整主视图控制器并将辅助视图控制器合并到折叠界面中。
Make sure to read up on the discussion part of that method for more specific details.
请务必阅读该方法的讨论部分以获取更具体的细节。
The way that Apple handles this is:
苹果处理这种情况的方式是:
- (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;
}
}
This implementation basically does the following:
此实现主要执行以下操作:
- If
secondaryViewController
is what we're expecting (aUINavigationController
), and it's showing what we're expecting (aDetailViewController
-- your view controller), but has no model (detailItem
), then "Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
" - Otherwise, return "
NO
to let the split view controller try and incorporate the secondary view controller's content into the collapsed interface"
- 如果
secondaryViewController
是我们期望的(aUINavigationController
),并且它显示了我们期望的(a——DetailViewController
你的视图控制器),但没有模型(detailItem
),那么“Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
” - 否则,返回“
NO
让拆分视图控制器尝试将辅助视图控制器的内容合并到折叠界面中”
The results are the following for the iPhone in portrait (either starting in portrait or rotating to portrait -- or more accurately compact size class):
纵向 iPhone 的结果如下(从纵向开始或旋转到纵向——或者更准确地说是紧凑型尺寸):
- If your view is correct
- and has a model, show the detail view controller
- but has no model, show the master view controller
- If your view is not correct
- show the master view controller
- 如果你的观点是正确的
- 并有一个模型,显示详细视图控制器
- 但没有模型,显示主视图控制器
- 如果您的观点不正确
- 显示主视图控制器
Clear as mud.
清如泥。
回答by Clifton Labrum
Here is the accepted answer in Swift. Just create this subclass and assign it to your splitViewController in your storyboard.
这是 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
}
}
回答by Ni?oScript
Swift version of Mark S' correct answer
Mark S 正确答案的 Swift 版本
As provided by Apple's Master-Detail template.
由 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
}
Clarification
澄清
(What Mark S said was slightly confusing)
(Mark S 说的有点令人困惑)
This delegate method is called splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:
, because that's what it does. When changing to a more compact width size (for example when rotating the phone from landscape to portrait), it needs to collapse the split view controller into only one of them.
这个委托方法被称为splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:
,因为它就是这样做的。当更改为更紧凑的宽度尺寸时(例如将手机从横向旋转到纵向时),它需要将拆分视图控制器折叠为其中之一。
This function returns a boolean to decide if it should collapse the Detail and show the Master or not.
这个函数返回一个布尔值来决定它是否应该折叠 Detail 并显示 Master。
So in our case, we'll decided based on if there was a detail selected or not. How do we know if our detail is selected? If we follow Apple's Master-Detail template, the detail view controller should have an optional variable having the detail info, so if it's nil (.None), there's nothing selected yet and we should show the Master so the user can select something.
所以在我们的例子中,我们将根据是否选择了一个细节来决定。我们如何知道我们的细节是否被选中?如果我们遵循 Apple 的 Master-Detail 模板,详细视图控制器应该有一个包含详细信息的可选变量,所以如果它是 nil (.None),则还没有选择任何内容,我们应该显示 Master,以便用户可以选择某些内容。
That's it.
就是这样。
回答by oli
From the documentation, you need to use a delegate to tell the UISplitViewController
notto incorporate the detail view into the "collapsed interface" (i.e. the "Portrait mode" in your case). In Swift 4, the delegate method to implement for that has been renamed:
从文档中,您需要使用委托来告诉UISplitViewController
不要将详细信息视图合并到“折叠界面”(即您的案例中的“纵向模式”)中。在 Swift 4 中,为此实现的委托方法已重命名:
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
return true
}
回答by Tony
My app was written in Swift 2.x and could run well. After converting it into Swift 3.0 (using XCode converter), it starts showing detail first instead of master in portrait mode. The problem is the name of the function splitViewController is not changed to match the new one of UISplitViewControllerDelegate.
我的应用程序是用 Swift 2.x 编写的,可以很好地运行。将其转换为 Swift 3.0(使用 XCode 转换器)后,它开始首先显示细节,而不是纵向模式下的母版。问题是函数 splitViewController 的名称没有更改为与新的 UISplitViewControllerDelegate 匹配。
After changing that function's name manually, my app now can work correctly:
手动更改该函数的名称后,我的应用程序现在可以正常工作:
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
}
回答by Gank
#import <UIKit/UIKit.h>
@interface SplitProductView : UISplitViewController<UISplitViewControllerDelegate>
@end
.m:
.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
回答by Hao-Cher Hong
If you don't have default values to show in detail view controller, you could just simply delete the default segue between the SplitViewController and your detail UIViewController in the story board. This will make it always goes into Master View Controller first.
如果您没有要在详细信息视图控制器中显示的默认值,您可以简单地删除 SplitViewController 和故事板中您的详细信息 UIViewController 之间的默认转场。这将使它始终首先进入主视图控制器。
The side effect of this is that instead of seeing two view in landscape, you will see one view in full size in SplitViewController until Show Detail Segue in master view controller fired.
这样做的副作用是,您不会在横向看到两个视图,而是在 SplitViewController 中看到一个完整大小的视图,直到主视图控制器中的 Show Detail Segue 被触发。
回答by Bartosz Kunat
For all the people who couldn't find cs193p's friday section:
对于所有找不到cs193p的星期五部分的人:
In Swift 3.1.1 creating a subclass of UISplitViewController and implementing one of its delegate methods worked for me like a charm:
在 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
} }
回答by Maik639
In my opinion you should solve this problem more generic. You can subclass the UISplitViewController and implement a protocol in the embedded view controllers.
在我看来,您应该更通用地解决这个问题。您可以继承 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
}
Example implementation in UITableViewController:
UITableViewController 中的示例实现:
extension SettingsTableViewController: SplitViewControllerCollapseProtocol {
func shouldShowMasterOnCollapse() -> Bool {
return tableView.indexPathForSelectedRow == nil
}
}
Hope it helps. So you can reuse this class and just need to implement a protocol.
希望能帮助到你。所以你可以重用这个类,只需要实现一个协议。
回答by Borys Shcherbyna
Just remove DetailViewController from SplitView controllers when you need it to start from Master.
当您需要从 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;
}