具有多个嵌入segue的ContainerView

51
有没有办法在一个 ContainerView 中使用多个嵌入式转场?目的是让 ContainerView 保存几个不同的 ViewControllers,具体取决于按下的按钮;一次只会显示一个。我想使用嵌入式转场,这样在界面生成器中,故事板会自动显示与 ContainerView 相同的大小。
我知道我可以手动调整界面生成器中其他 ViewControllers 的大小,但我想要由嵌入式转场提供的自动调整大小。如果有其他方法可以做到这一点,也可以。视图在 viewDidLoad 上加载也没问题 - 如前所述,显示的 ViewController 可能会根据按下的按钮改变。

1
我知道这不是你要求的,但作为另一种解决方法,你可以使用多个ContainerView,它们重叠并具有相同的大小,每个都有自己的自定义segue。然后,在代码中,根据你想要显示哪个子UIViewController,使用[self performSegueWithIdentifier:]执行正确的segue。至少,这样做可以保持自动调整大小、在IB中保持连接和保持prepareForSegue逻辑。 - Carlos P
7个回答

50

不,无法将多个嵌入式segue指向同一个容器视图。在IB中完成所有设置的一种方法是使嵌入控制器成为UITabBarController(隐藏选项卡栏)。然后,您可以在选项卡中拥有任意数量的控制器,并使用UITabBarController的selectedIndex属性在代码中切换到它们。


在询问之前,我四处寻找了很久,但我想我一直希望自己错过了什么。哦,好吧,我只能接受视图大小不正确的事实了。(你关于使用TabBarController的想法很有趣,但不幸的是我的ContainerView已经在另一个TabBarController中了)。 - Steve Haley
14
我知道这是一个旧答案。我解决这个问题的方式是使用多个视图容器,但一次只显示一个。不确定这是否是最好的方法,但它可以正常工作。 - Nuno Gonçalves
@NunoGonçalves,你是怎么做到的?能帮我一下吗? - Charmi
@Charmi 这是一年前的事了。但我想我添加了2个容器,每个都有一个segue,并根据需要隐藏每个容器。隐藏容器会隐藏内容,所以我得到了我想要的行为。 - Nuno Gonçalves
这是一个优雅的解决方案,我正在使用它。如果有人需要帮助隐藏选项卡栏,我在这个问题上有个解决方案:https://dev59.com/gmUp5IYBdhLWcg3wZHAc#45291118 - Mark Suman
显示剩余2条评论

17

是的,我能够实现你所想要的,受 @rdelmar 的帖子启发。你需要做的是将UITabBarViewController嵌入到你的容器视图中。然后通过编程方式选择你想要展示的控制器。你可能还想隐藏选项卡栏。

间接包含更多视图的容器视图

如果你愿意,你也可以在Storyboard文件中隐藏选项卡栏。

你可以通过继承UITabBarController来选择你想要展示的视图控制器:

override func viewDidLoad() {
    super.viewDidLoad()
    self.selectedIndex = 1
}
您可以在视图控制器中调用self.tabBarController?.tabBar.isHidden = true来隐藏选项卡栏,具体位置是在viewDidLoad()函数中。

这很有用,但是我如何从我的主VC(包含ContainerView)中选择一个项目?我已经尝试在viewDidLoad()中放置self.tabBarController?.selectedIndex = 5,但它不起作用): - Arnaldo
2
你可以在 'prepareForSegue' 方法中实现此功能,代码类似于这样:'guard let tabBarVC = segue.destination as? UITabBarController else {return}',然后再加上 'tabBarVC.selectedIndex = 5'。由于我是用手机打字,可能有些错误,请见谅。 - Andrej
谢谢,兄弟!帮了我一个大忙。 - Arnaldo

15

我知道这个问题有点老,但是如果你还在寻找答案,或者其他人发现了这个问题,我希望能够回答。我曾经遇到过类似的问题,后来解决了。

简而言之,你需要三层:
- 一个外部视图控制器("ExternalViewController")
- 一个视图控制器管理器("ViewControllerManager")
- 你实际想要在它们之间切换的子视图控制器("ChildViewController")

在ExternalViewController中使用一个容器视图,并使用嵌入式segue将其连接到ViewControllerManager。然后,ViewControllerManager会以编程方式持有其他ChildViewControllers,如此Apple文档中所述,特别是添加和删除子项的部分。

当你添加一个子视图控制器时,将其框架设置为与ViewControllerManager的框架相同(因为你正在ViewControllerManager内部执行此操作,所以将子项的框架设置为self.view.frame)。当然,你还需要一些逻辑和外部控件在ExternalViewController内部进行切换。

希望这可以帮助你!


14

这是答案,是的,根据链接是可能的。 - 0xFK
1
这对我来说效果更好。选项卡视图控制器的主要缺点是每个选项卡的VC在不可见时仍然存在。当它们不可见时,我希望它被释放并且如果需要重新初始化。 - Chuck H
2
链接已售罄 :( - jeet.chanchawat
4
我使用Wayback Machine在这个链接中找到了上述的内容:https://web.archive.org/web/20170605234158/http://sandmoose.com/post/35714028270/storyboards-with-custom-container-view-controllers。这是一个很棒的解决方案,作者在一个GitHub项目(https://github.com/mluton/EmbeddedSwapping)中发布了代码,该项目仍然存在。 - brotskydotcom

4
我是通过使用-shouldPerformSegueWithIdentifier:sender:实现的。我有一个容器,传递了一个对象,根据此对象的类型来决定显示哪个子视图控制器。
这种结构似乎过于复杂,但是使得基础视图控制器可以忽略我所拥有的不同类型的任务,只需由容器视图控制器处理。
容器视图控制器有多个容器视图,只有根据任务类型才会执行segue。
我不知道是否可以通过调用-performSegueWithIdentifier:sender:手动执行嵌入segue,但这也可能是一种可能性。

2
我也曾经艰难地解决过这个问题。我有不同但相似的嵌入式表视图控制器,根据在视图控制器中设置的参数,我想要展示它们。我的解决方法是在视图控制器中放置一个带有IBOutlet的嵌入式容器,并在IB中设置容器的大小约束。然而,在IB中不要进行任何嵌入Segue操作。然后,在viewDidLoad方法中,我以编程方式添加正确的视图控制器,并将其边缘固定到嵌入式容器上。
这种方法的核心代码如下(Swift 4):
extension UIView {
    func pinToParent() {
        self.translatesAutoresizingMaskIntoConstraints = false
        let attributes: [NSLayoutAttribute] = [.top, .bottom, .right, .left]
        NSLayoutConstraint.activate(attributes.map {
            NSLayoutConstraint(item: self, attribute: $0, relatedBy: .equal, toItem: self.superview, attribute: $0, multiplier: 1, constant: 0)
        })
    }
}

class ColorVC: UIViewController {

    @IBOutlet weak var tableContainer: UIView!
    var color : rgb = .red

    fileprivate var colorTableVC : ColorTableVC?

    override func viewDidLoad() {
        super.viewDidLoad()

        switch color {
        case .red:
            colorTableVC = RedTableVC.init(style: .plain)
        case .green:
            colorTableVC = GreenTableVC.init(style: .plain)
        case .blue:
            colorTableVC = BlueTableVC.init(style: .plain)
        }
        if let vc = colorTableVC {
            if (vc.view) != nil {
                self.addChildViewController(vc)
                tableContainer.addSubview(vc.view)
                vc.view.pinToParent()
                vc.didMove(toParentViewController: self)
            }
        }
    }
}

在ColorVC中,可以看到container IBOutlet和由主表视图控制器设置的"color"参数。RedTableVC、GreenTableVC和BlueTableVC都是从ColorTableVC子类化而来,该子类又从UITableViewController子类化而来。这个共同的继承让我可以使用一个"colorTableVC"变量来指向任何已实例化的控制器。(不是完全必要的)。但这避免了在下面复制代码以添加视图到层次结构并将新控制器固定到容器视图。在顶部,我创建了一个UIView扩展来将视图固定到其父边缘。
以下图片显示了如何在IB中设置项目,特别是右侧的视图控制器。对于这个示例,我将"嵌入式"控制器的高度设为主视图控制器高度的一半 - 因此当您旋转设备时,可以看到在IB中设置的约束确实被应用。

Embedded controller IB setup


-1
在您的工作视图控制器中拖动一个UIView并将其约束到安全区域(iPhone X系列)的顶部、前导、尾随和底部。
现在在该UIView内放置容器视图(内容视图)。 在该UIView内放置尽可能多的内容视图,并嵌入到相应的ViewControllers中。
对我有用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接