Как добавить вид контейнера программно
Контейнерный вид можно легко добавить в раскадровку через Редактор интерфейсов. При добавлении Container View имеет вид замещающего, встроенный segue и (дочерний) контроллер представления.
Тем не менее, я не могу найти способ добавления Container View программно. На самом деле, я даже не могу найти класс с именем UIContainerView
или так.
Название класса Container View, безусловно, хорошее начало. Полное руководство, в том числе segue, будет высоко оценено.
Мне известно о руководстве по программированию View Controller, но я не считаю его таким же, как способ создания интерфейса Builder для Container Viewer. Например, когда ограничения установлены правильно, представление (дочернее) будет адаптировано к изменениям размера в представлении контейнера.
Ответы
Ответ 1
Раскадровка "контейнерный вид" - это просто стандартный объект UIView
. Специального типа "просмотр контейнера" не существует. Фактически, если вы посмотрите на иерархию представлений, вы увидите, что "представление контейнера" является стандартным UIView
:
![container view]()
Чтобы достичь этого программно, вы используете "просмотр содержимого контроллера":
- Создайте дочерний контроллер представления, вызвав
instantiateViewController(withIdentifier:)
для объекта раскадровки. - Вызовите
addChild
в вашем родительском контроллере представления. - Добавьте представление контроллера
view
в вашу иерархию представлений с помощью addSubview
(а также установите соответствующий frame
или ограничения). - Вызовите метод
didMove(toParent:)
на дочернем контроллере представления, передав ссылку на родительский контроллер представления.
Посмотрите Реализацию Контроллера Представления Контейнера в Руководстве по программированию Контроллера Представления и разделе "Реализация Контроллера Представления Контейнера" Справочника по классу UIViewController.
Например, в 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)
}
Обратите внимание, что вышеприведенное фактически не добавляет "представление контейнера" в иерархию. Если вы хотите сделать это, вы бы сделали что-то вроде:
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)
}
Этот последний шаблон чрезвычайно полезен, если когда-либо происходит переход между различными контроллерами дочерних представлений, и вы просто хотите убедиться, что одно дочернее представление находится в том же месте и в предыдущем дочернем представлении (то есть все уникальные ограничения для размещения диктуются представлением контейнера, вместо того, чтобы каждый раз восстанавливать эти ограничения). Но если просто выполнить простое сдерживание представления, потребность в этом отдельном представлении контейнера менее убедительна.
В приведенных выше примерах установка Im translatesAutosizingMaskIntoConstraints
значениеAutosizingMaskIntoConstraints в false
определяя ограничения самостоятельно. Очевидно, что вы можете оставить translatesAutosizingMaskIntoConstraints
как true
и установить как frame
и autosizingMask
для добавляемых представлений, если хотите.
Смотрите предыдущие редакции этого ответа для версий Swift 3 и Swift 2.
Ответ 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)
Ответ 3
подробности
- Xcode 10.2 (10E125), Swift 5
Решение
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()
}
}
использование
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)
}
}
Полный образец
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)")
}
}
Результаты
![enter image description here]()
Ответ 4
Вот мой код в swift 3, также работайте в swift 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
}
Используйте другую функцию встраивания с контроллером просмотра без раскадровки.