【问题标题】:UISplitViewController - dismiss / pop Detail View Controller in code in collapsed modeUISplitViewController - 在代码中以折叠模式关闭/弹出详细视图控制器
【发布时间】:2025-12-01 16:00:02
【问题描述】:

从 iOS8 开始,我们可以在紧凑型和普通设备上使用 UISplitViewController。这很棒,因为我不必为 iPhone 和 iPad 创建两个不同的故事板,但是我遇到了一个问题。

如果拆分视图控制器在 iPad 上(如果折叠属性为 NO),我可以简单地调用它以在左侧显示 MasterVC。

self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;
[self.splitViewController.displayModeButtonItem action];

但如果它在 iPhone 上(如果折叠属性为 YES),则 displayMode 将被忽略,并且不执行任何操作。

我无法使用 popToRootViewControllerAnimated 弹出 DetailVC,因为 DetailVC 有它自己的导航控制器。

如果没有像 dismissViewControllerAnimated:completion: 这样的方法用于显示详细信息的视图控制器,Apple 如何期望我们在折叠模式的代码中显示 MasterVC(dismiss DetailVC)?您的帮助将不胜感激。谢谢

【问题讨论】:

    标签: ios objective-c iphone uisplitviewcontroller


    【解决方案1】:

    在不支持“拆分”模式的设备上,如果

    1. UISplitViewController 第一次加载,然后返回YES您的委托类 (UISplitViewControllerDelegate) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: 方法方法应该这样做:

      - (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
          return YES;
      }
      
    2. 您希望在特定事件(例如,触摸按钮)之后关闭 detail 视图控制器回到。在这种情况下,您必须弹出 detail 视图控制器导航控制器:

      [detailViewController.navigationController.navigationController popToRootViewControllerAnimated:YES]
      

    【讨论】:

    • 感谢您的回复!我完全忘记了委托方法。但我的已经返回YES。我正在尝试在某个事件中关闭 DetailVC/显示 MasterVC。我正在为 DetailVC 寻找类似 @​​987654328@ 的东西。但如果有任何事情,我会更多地检查委托。
    • @SFF 你试过弹出细节导航控制器父导航控制器吗?类似[detailViewController.navigationController.navigationController popToRootViewControllerAnimated]
    • 我不知道我能做到这一点!这对我有用。您介意将其添加到答案中吗?非常感谢。
    • 执行此操作时是否有人收到此警告日志? Unbalanced calls to begin/end appearance transitions for <Your Detail View Controller>
    【解决方案2】:

    今天尝试从拆分视图控制器中的详细视图弹出时遇到了类似的问题。

    虽然我确信接受的答案可以正常工作,但我发现另一种同样有效且可能更简洁的方法是使用 unwind segue。

    我在我想返回的主视图上设置了一个展开转场,然后从我想弹出的视图创建了一个转场链接到展开转场(注意:假设您正在使用情节提要)。

    确保在您要返回的目标视图上设置 IBAction:

    -(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue { }
    

    将出口连接到情节提要中的转场,以展开转场。抱歉,我没有提供很多关于如何设置 unwind segue 的详细信息,但是有很多可用的教程。

    然后在您要关闭的控制器上,将一个 segue 连接到您要弹回的控制器的 unwind segue。请务必命名segue。

    然后在要关闭的视图控制器中触摸按钮,只需调用

    [self performSegueWithIdentifier:@"unwindSegueName" sender:self];
    

    这非常有效,并且避免了向后挖掘可能会改变的导航层次结构。

    希望这对某人有用! 节日快乐!

    【讨论】:

    • 谢谢你。我从an answer to a similar question 得到的一个细节是,只有在UISplitViewController 当前已折叠(并且确实在未折叠的控制器上尝试它会导致崩溃)时进行展开转场才有意义,因此将调用放入 if (self.splitViewController.isCollapsed) block 使其适用于所有配置。
    • 自适应转场的关键在于它们在这两种情况下都可以工作,无需检查是否折叠。
    【解决方案3】:

    如果我们处于折叠状态(iPhone 不包括 +sizes),我最终会弹出 DetailVC,如果我们不处于折叠状态 (iPad),则显示/隐藏 MasterVC。

    @IBAction func backTouchUp(_ sender: UIButton) {
        if let splitViewController = splitViewController,
            !splitViewController.isCollapsed {
            UIApplication.shared.sendAction(splitViewController.displayModeButtonItem.action!, to: splitViewController.displayModeButtonItem.target, from: nil, for: nil)
        } else {
            navigationController?.popViewController(animated: true)
        }
    }
    

    【讨论】:

      【解决方案4】:

      谢谢pNre!下面的代码将处理在折叠时显示自定义后退按钮以及在未折叠时显示 displayModeButton

      lazy var backButtonItem: UIBarButtonItem = {
          UIBarButtonItem(image: UIImage(named: "backImage"), style: .plain, target: self, action: #selector(dismissAnimated))
      }()
      
      override func viewWillLayoutSubviews() {
          super.viewWillLayoutSubviews()
      
          guard let svc = splitViewController else { return }
      
          if svc.isCollapsed {
              navigationItem.leftBarButtonItem = backButtonItem
          } else {
              navigationItem.leftBarButtonItem = svc.displayModeButtonItem
          }
      }
      
      func dismissAnimated() {
          _ = navigationController?.navigationController?.popViewController(animated: true)
      }
      

      我已将其放置在 willLayoutSubviews() 而不是 viewDidLoad() 中,以便自适应更新按钮,例如,针对 iPhone 7 Plus 上的方向更改和尺寸等级更改(例如在 iPad 上的拆分视图中)。

      【讨论】: