ContainerView with multiple embed segues

2020-05-12 11:47发布

Is there a way to have a single ContainerView with multiple embed segues? The aim is for a ContainerView to hold a few different ViewControllers depending on what buttons have been pressed; only one is going to be visible at a time. I want to use embed segues so that in Interface Builder the storyboards are automatically shown at the same size as the ContainerView.

I realise that I can manually resize the other ViewControllers in InterfaceBuilder, but I want the automatic sizing provided by the embed segue. If another way of doing that is available, that would be fine too. Not having the views load on viewDidLoad is fine - as mentioned earlier, the shown ViewController can change depending on the buttons pressed.

7条回答
手持菜刀,她持情操
2楼-- · 2020-05-12 12:22

Yes, I was able to achieve what you're looking for inspired by @rdelmar post. What you need to do is to embed a UITabBarViewController into your container view. Then you programmatically choose which controller you like to present. You might also like to hide the tab bar.

Container view indirectly containing more views

If you want you can also hide the tab bars seen in the storyboard file

You can choose the view controller you want to present by subclassing the UITabBarController:

override func viewDidLoad() {
    super.viewDidLoad()
    self.selectedIndex = 1
}

You can hide the tab bar in your view controller by calling self.tabBarController?.tabBar.hidden = truein viewDidLoad().

查看更多
Rolldiameter
3楼-- · 2020-05-12 12:22

I found this wonderful article that explained exactly how to do it: http://sandmoose.com/post/35714028270/storyboards-with-custom-container-view-controllers

you get your container and can call any view controller behind, there is a bit of set up to have everything linked but once done, you get a still useable storyboard.

查看更多
甜甜的少女心
4楼-- · 2020-05-12 12:31

In your working viewController drag a uiview and set constraint to top, leading, trailing, bottom to safe area (Iphone X serise).

Now put Container view (Content View) inside that UIView. Put as many content views as you want inside that UIView and embed to respective ViewContrllers.

Worked for me.

查看更多
疯言疯语
5楼-- · 2020-05-12 12:42

I've achieved that by making use of -shouldPerformSegueWithIdentifier:sender:. I have a container that is passed an object and depending on the type of this object decides which child view controller to show.

The structure seems a little over complicated but allows the base view controller to ignore the different types of tasks I have, leaving that to the container view controller. The container view controller has then multiple container views which segues are only performed depending on the type of task.

I don't know if you can actually perform the embed segues manually by calling -performSegueWithIdentifier:sender: but that could also be a possibility.

查看更多
Juvenile、少年°
6楼-- · 2020-05-12 12:45

No, there is no way to have multiple embed segues to one container view. One way to do all the setup in IB, would be to make the embedded controller a UITabBarController (with the tab bar hidden). You can then have as many controllers in the tabs as you want, and switch to them in code using the selectedIndex property of UITabBarController.

查看更多
▲ chillily
7楼-- · 2020-05-12 12:47

I struggled with this for a long time, too. I had the case where I had different, but similar embedded table view controllers that I wanted to show depending on a parameter that was set in the segue to the view controller. What worked was to put in the embedded container with an IBOutlet in the view controller. The container can have size constraints set in IB. However, don't make any embed segues in IB. Then in viewDidLoad, I programmatically add the correct view controller and pin its edges to the embed container.

The heart of this approach is seen in the following code (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)
            }
        }
    }
}

In the ColorVC, one sees the container IBOutlet and the "color" parameter set by the main table view controller. The RedTableVC, GreenTableVC, and BlueTableVC are all subclassed from ColorTableVC which is sub-classed from UITableViewController. The common heritage lets me use one "colorTableVC" variable to point to any of the instantiated controllers. (Not entirely necessary). But this does avoid duplicating code below to add the view in the heirarchy and pin the new controller to the container view. At the top, I made an extension on UIView to pin a view to its parents edges.

The following image shows how the project and particularly the view controller on the right was set up in IB. For this example, I made the height of the "embedded" controller half the height of the main view controller - so when you rotate the device, one can see that the constraints set in IB are indeed applied.

Embedded controller IB setup

查看更多
登录 后发表回答