ios 如何以编程方式添加容器视图
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37370801/
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
How to add a Container View programmatically
提问by Dante May Code
A Container View can be easily added into a storyboard through Interface Editor. When added, a Container View is of a placeholder view, an embed segue, and a (child) view controller.
容器视图可以通过界面编辑器轻松添加到故事板中。添加后,容器视图具有占位符视图、嵌入转场和(子)视图控制器。
However, I am not able to find a way to add a Container View programmatically. Actually, I am not even able to find a class named UIContainerView
or so.
但是,我无法找到以编程方式添加容器视图的方法。实际上,我什至无法找到一个名为“orso”的类UIContainerView
。
A name for the class of Container View is surely a good start. A complete guide including the segue will be much appreciated.
Container View 类的名称无疑是一个好的开始。包括 segue 的完整指南将不胜感激。
I am aware of View Controller Programming Guide, but I do not regard it as the same as the way Interface Builder does for Container Viewer. For example, when the constraints are properly set, the (child) view will adapts to the size changes in Container View.
我知道 View Controller Programming Guide,但我不认为它与 Interface Builder 为 Container Viewer 所做的方式相同。例如,当约束设置正确时,(子)视图将适应容器视图中的大小变化。
回答by Rob
A storyboard "container view" is just a standard UIView
object. There is no special "container view" type. In fact, if you look at the view hierarchy, you can see that the "container view" is a standard UIView
:
故事板“容器视图”只是一个标准UIView
对象。没有特殊的“容器视图”类型。事实上,如果您查看视图层次结构,您可以看到“容器视图”是一个标准UIView
:
To achieve this programmatically, you employ "view controller containment":
为了以编程方式实现这一点,您使用“视图控制器包含”:
- Instantiate the child view controller by calling
instantiateViewController(withIdentifier:)
on the storyboard object. - Call
addChild
in your parent view controller. - Add the view controller's
view
to your view hierarchy withaddSubview
(and also set theframe
or constraints as appropriate). - Call the
didMove(toParent:)
method on the child view controller, passing the reference to the parent view controller.
- 通过调用
instantiateViewController(withIdentifier:)
storyboard 对象实例化子视图控制器 。 - 调用
addChild
您的父视图控制器。 - 将视图控制器添加
view
到您的视图层次结构中addSubview
(并frame
根据需要设置或 约束)。 - 调用
didMove(toParent:)
子视图控制器上的方法,将引用传递给父视图控制器。
See Implementing a Container View Controllerin the View Controller Programming Guideand the "Implementing a Container View Controller" section of the UIViewController Class Reference.
请参阅实现一个容器视图控制器的视图控制器编程指南和“实现一个容器视图控制器”一节的UIViewController类参考。
For example, in Swift 4.2 it might look like:
例如,在 Swift 4.2 中它可能看起来像:
override func viewDidLoad() {
super.viewDidLoad()
let controller = storyboard!.instantiateViewController(withIdentifier: "Second")
addChild(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(toParent: self)
}
Note, the above doesn't actually add a "container view" to the hierarchy. If you want to do that, you'd do something like:
请注意,以上实际上并未向层次结构添加“容器视图”。如果你想这样做,你会做这样的事情:
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")
addChild(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(toParent: self)
}
This latter pattern is extremely useful if ever transitioning between different child view controllers and you just want to make sure one child's view is in the same location and the previous child's view (i.e. all the unique constraints for the placement are dictated by the container view, rather than needing to rebuild these constraints each time). But if just performing simple view containment, the need for this separate container view is less compelling.
如果在不同的子视图控制器之间进行转换,并且您只想确保一个子视图的位置与前一个子视图的位置相同(即,放置的所有唯一约束都由容器视图指定,则后一种模式非常有用,而不是每次都需要重建这些约束)。但如果只是执行简单的视图包含,对这个单独的容器视图的需求就不那么引人注目了。
In the examples above, I'm setting translatesAutosizingMaskIntoConstraints
to false
defining the constraints myself. You obviously can leave translatesAutosizingMaskIntoConstraints
as true
and set both the frame
and the autosizingMask
for the views you add, if you'd prefer.
在上面的示例中,我设置translatesAutosizingMaskIntoConstraints
为false
自己定义约束。如果您愿意,您显然可以保留translatesAutosizingMaskIntoConstraints
astrue
并为您添加的视图设置 theframe
和 the autosizingMask
。
See previous revisions of this answer for Swift 3and Swift 2renditions.
回答by Bright Future
@Rob's answer in Swift 3:
@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)
回答by Vasily Bodnarchuk
Details
细节
- Xcode 10.2 (10E125), Swift 5
- Xcode 10.2 (10E125),Swift 5
Solution
解决方案
import UIKit
class WeakObject {
weak var object: AnyObject?
init(object: AnyObject) { self.object = object}
}
class EmbedController {
private weak var rootViewController: UIViewController?
private var controllers = [WeakObject]()
init (rootViewController: UIViewController) { self.rootViewController = rootViewController }
func append(viewController: UIViewController) {
guard let rootViewController = rootViewController else { return }
controllers.append(WeakObject(object: viewController))
rootViewController.addChild(viewController)
rootViewController.view.addSubview(viewController.view)
}
deinit {
if rootViewController == nil || controllers.isEmpty { return }
for controller in controllers {
if let controller = controller.object {
controller.view.removeFromSuperview()
controller.removeFromParent()
}
}
controllers.removeAll()
}
}
Usage
用法
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)
}
}
Full sample
完整样品
ViewController
视图控制器
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
带按钮的视图控制器
import UIKit
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)")
}
}
Results
结果
回答by Jeffrey Chen
Here is my code in swift 5.
这是我在 swift 5 中的代码。
class ViewEmbedder {
class func embed(
parent:UIViewController,
container:UIView,
child:UIViewController,
previous:UIViewController?){
if let previous = previous {
removeFromParent(vc: previous)
}
child.willMove(toParent: parent)
parent.addChild(child)
container.addSubview(child.view)
child.didMove(toParent: 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(toParent: nil)
vc.view.removeFromSuperview()
vc.removeFromParent()
}
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.children.first
)
completion?(vc)
}
}
}
Usage
用法
@IBOutlet weak var container:UIView!
ViewEmbedder.embed(
withIdentifier: "MyVC", // Storyboard ID
parent: self,
container: self.container){ vc in
// do things when embed complete
}
Use the other embed function with non-storyboard view controller.
将其他嵌入功能与非故事板视图控制器一起使用。