如何以编程方式添加容器视图

容器视图可以通过界面编辑器轻松添加到故事板中。 当添加时,Container View是占位符视图,embeddedSegue和(子)视图控制器。

但是,我无法find以编程方式添加Container视图的方法。 其实,我甚至无法find一个名为UIContainerView的类。

Container View类的名字肯定是一个好的开始。 包括segue的完整指南将不胜感激。

我知道“视图控制器编程指南”,但我不认为它与Interface Builder为Container Viewer所做的一样。 例如,当约束设置正确时,(子)视图将适应容器视图中的大小更改。

故事板“容器视图”只是一个标准的UIView对象。 没有特殊的“容器视图”types。 实际上,如果您查看视图层次结构,则可以看到“容器视图”是标准的UIView

容器视图

为了达到这个目的,你使用“view controller containment”:

  • 通过调用storyboard对象上的instantiateViewController(withIdentifier:)instantiateViewController(withIdentifier:)视图控制器。
  • 在父视图控制器中调用addChildViewController
  • 使用addSubview将视图控制器的view添加到视图层次结构中(还可以根据需要设置frame或约束)。
  • 调用子视图控制器上的didMove(toParentViewController:)方法,将引用传递给父视图控制器。

请参阅“ View Controller编程指南”中的“实现容器视图控制器”和“ UIViewController类参考 ”的“实现容器视图控制器”部分


例如,在Swift 3中,它可能看起来像这样:

 override func viewDidLoad() { super.viewDidLoad() let controller = storyboard!.instantiateViewController(withIdentifier: "Second") addChildViewController(controller) controller.view.translatesAutoresizingMaskIntoConstraints = false view.addSubview(controller.view) NSLayoutConstraint.activate([ controller.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), controller.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), controller.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10) ]) controller.didMove(toParentViewController: self) } 

请注意,上述内容实际上并没有将“容器视图”添加到层次结构中。 如果你想这样做,你会做这样的事情:

 override func viewDidLoad() { super.viewDidLoad() // add container let containerView = UIView() containerView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(containerView) NSLayoutConstraint.activate([ containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), ]) // add child view controller view to container let controller = storyboard!.instantiateViewController(withIdentifier: "Second") addChildViewController(controller) controller.view.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(controller.view) NSLayoutConstraint.activate([ controller.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), controller.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), controller.view.topAnchor.constraint(equalTo: containerView.topAnchor), controller.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) ]) controller.didMove(toParentViewController: self) } 

如果在不同的子视图控制器之间进行转换,并且只想确保一个子视图位于同一位置和前一个子视图(即所有对该位置的唯一约束都由容器视图指定,而不是每次都需要重build这些约束)。 但是,如果只是执行简单的视图遏制,这个单独的容器视图的需要就不那么引人注目了。


对于Swift 2演绎,请参阅此答案的以前版本 。

@ Rob在Swift 3中的回答:

  // add container let containerView = UIView() containerView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(containerView) NSLayoutConstraint.activate([ containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10), containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10), containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), ]) // add child view controller view to container let controller = storyboard!.instantiateViewController(withIdentifier: "Second") addChildViewController(controller) controller.view.translatesAutoresizingMaskIntoConstraints = false containerView.addSubview(controller.view) NSLayoutConstraint.activate([ controller.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), controller.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), controller.view.topAnchor.constraint(equalTo: containerView.topAnchor), controller.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) ]) controller.didMove(toParentViewController: self) 

细节

xCode:9.1,Swift 4

 class EmbedController { public private(set) weak var rootViewController: UIViewController? public private(set) var controllers = [UIViewController]() init (rootViewController: UIViewController) { self.rootViewController = rootViewController } func append(viewController: UIViewController) { if let rootViewController = self.rootViewController { controllers.append(viewController) rootViewController.addChildViewController(viewController) rootViewController.view.addSubview(viewController.view) } } deinit { if self.rootViewController != nil { for controller in controllers { controller.view.removeFromSuperview() controller.removeFromParentViewController() } controllers.removeAll() self.rootViewController = nil } } } 

用法

 class SampleViewController: UIViewController { private var embedController: EmbedController? override func viewDidLoad() { super.viewDidLoad() embedController = EmbedController(rootViewController: self) let newViewController = ViewControllerWithButton() newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 150), size: CGSize(width: 200, height: 80)) newViewController.view.backgroundColor = .lightGray embedController?.append(viewController: newViewController) } } 

完整的样品

视图控制器

 import UIKit class ViewController: UIViewController { private var embedController: EmbedController? private var button: UIButton? private let addEmbedButtonTitle = "Add embed" override func viewDidLoad() { super.viewDidLoad() button = UIButton(frame: CGRect(x: 50, y: 50, width: 150, height: 20)) button?.setTitle(addEmbedButtonTitle, for: .normal) button?.setTitleColor(.black, for: .normal) button?.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) view.addSubview(button!) print("viewDidLoad") printChildViewControllesInfo() } func addChildViewControllers() { var newViewController = ViewControllerWithButton() newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 150), size: CGSize(width: 200, height: 80)) newViewController.view.backgroundColor = .lightGray embedController?.append(viewController: newViewController) newViewController = ViewControllerWithButton() newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 250), size: CGSize(width: 200, height: 80)) newViewController.view.backgroundColor = .blue embedController?.append(viewController: newViewController) print("\nChildViewControllers added") printChildViewControllesInfo() } @objc func buttonTapped() { if embedController == nil { embedController = EmbedController(rootViewController: self) button?.setTitle("Remove embed", for: .normal) addChildViewControllers() } else { embedController = nil print("\nChildViewControllers removed") printChildViewControllesInfo() button?.setTitle(addEmbedButtonTitle, for: .normal) } } func printChildViewControllesInfo() { print("view.subviews.count: \(view.subviews.count)") print("childViewControllers.count: \(childViewControllers.count)") } } 

ViewControllerWithButton

 class ViewControllerWithButton:UIViewController { override func viewDidLoad() { super.viewDidLoad() } private func addButon() { let buttonWidth: CGFloat = 150 let buttonHeight: CGFloat = 20 let frame = CGRect(x: (view.frame.width-buttonWidth)/2, y: (view.frame.height-buttonHeight)/2, width: buttonWidth, height: buttonHeight) let button = UIButton(frame: frame) button.setTitle("Button", for: .normal) button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) view.addSubview(button) } override func viewWillLayoutSubviews() { addButon() } @objc func buttonTapped() { print("Button tapped in \(self)") } } 

结果

在这里输入图像说明 在这里输入图像说明 在这里输入图像说明

这里是我的代码在迅速3,也在迅速工作4。

 class ViewEmbedder { class func embed( parent:UIViewController, container:UIView, child:UIViewController, previous:UIViewController?){ if let previous = previous { removeFromParent(vc: previous) } child.willMove(toParentViewController: parent) parent.addChildViewController(child) container.addSubview(child.view) child.didMove(toParentViewController: parent) let w = container.frame.size.width; let h = container.frame.size.height; child.view.frame = CGRect(x: 0, y: 0, width: w, height: h) } class func removeFromParent(vc:UIViewController){ vc.willMove(toParentViewController: nil) vc.view.removeFromSuperview() vc.removeFromParentViewController() } class func embed(withIdentifier id:String, parent:UIViewController, container:UIView, completion:((UIViewController)->Void)? = nil){ let vc = parent.storyboard!.instantiateViewController(withIdentifier: id) embed( parent: parent, container: container, child: vc, previous: parent.childViewControllers.first ) completion?(vc) } } 

用法

 @IBOutlet weak var container:UIView! ViewEmbedder.embed( withIdentifier: "MyVC", // Storyboard ID parent: self, container: self.container){ vc in // do things when embed complete } 

使用非故事板视图控制器的其他embeddedfunction。