프로그래밍 방식으로 컨테이너보기를 추가하는 방법
Container View는 Interface Editor를 통해 스토리 보드에 쉽게 추가 할 수 있습니다. 추가되면 컨테이너보기는 플레이스 홀더보기, 임베드 된 세그먼트 및 (하위)보기 컨트롤러입니다.
그러나 프로그래밍 방식으로 컨테이너보기를 추가하는 방법을 찾을 수 없습니다. 사실, 나는 그런 클래스를 찾을 수조차 없다 UIContainerView
.
Container View 클래스의 이름은 확실히 좋은 시작입니다. segue를 포함한 완전한 가이드를 많이 주시면 감사하겠습니다.
View Controller Programming Guide를 알고 있지만 Interface Builder가 Container Viewer에 대해 수행하는 방식과 동일하지 않다고 생각합니다. 예를 들어 제약 조건이 제대로 설정되면 (하위)보기가 컨테이너보기의 크기 변경에 적응합니다.
스토리 보드 "컨테이너보기"는 표준 UIView
개체 일뿐 입니다. 특별한 "컨테이너보기"유형은 없습니다. 실제로 뷰 계층 구조를 보면 "컨테이너 뷰"가 표준임을 알 수 있습니다 UIView
.
이를 프로그래밍 방식으로 달성하려면 "뷰 컨트롤러 포함"을 사용합니다.
instantiateViewController(withIdentifier:)
스토리 보드 개체를 호출하여 자식 뷰 컨트롤러를 인스턴스화 합니다.addChild
부모 뷰 컨트롤러를 호출 하십시오.- 뷰 컨트롤러
view
를 뷰 계층 구조에 추가하고 또는 제약 조건을 적절하게addSubview
설정하십시오frame
. didMove(toParent:)
자식보기 컨트롤러 에서 메서드를 호출하여 부모보기 컨트롤러에 대한 참조를 전달합니다.
보기 컨테이너보기 컨트롤러를 구현 에 Programming Guide를보기 컨트롤러 와의 섹션 "컨테이너 뷰 컨트롤러 구현" 의 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)
}
이 후자의 패턴은 서로 다른 자식보기 컨트롤러간에 전환 할 때 매우 유용하며 한 자식의보기가 동일한 위치와 이전 자식보기에 있는지 확인하려는 경우 (즉, 배치에 대한 모든 고유 한 제약 조건은 컨테이너보기에 의해 결정됩니다. 매번 이러한 제약 조건을 재 구축 할 필요가 없습니다). 그러나 단순한보기 포함 만 수행하는 경우이 별도의 컨테이너보기에 대한 필요성은 덜 매력적입니다.
위의 예에서, 내가하고 있어요 translatesAutosizingMaskIntoConstraints
에 false
제약 자신을 정의. 당신은 분명히 떠날 수 translatesAutosizingMaskIntoConstraints
로 true
하고 모두 설정 frame
하고를 autosizingMask
원하는 경우 표시 추가,보기에.
Swift 3 및 Swift 2 변환에 대한이 답변의 이전 개정판을 참조하십시오 .
Swift 3에서 @Rob의 답변 :
// 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 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)")
}
}
결과
다음은 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
}
스토리 보드가 아닌 뷰 컨트롤러와 함께 다른 임베드 기능을 사용하십시오.
참고 URL : https://stackoverflow.com/questions/37370801/how-to-add-a-container-view-programmatically
'Programing' 카테고리의 다른 글
Javascript / jQuery : 다중 선택에서 값 설정 (선택) (0) | 2020.09.06 |
---|---|
Swift에서 술어 사용 (0) | 2020.09.06 |
sed를 사용하여 줄의 처음 'N'문자를 어떻게 인쇄합니까? (0) | 2020.09.06 |
Jackson을 사용하여 JSON 문자열을 배열로 구문 분석하는 방법 (0) | 2020.09.06 |
공백이없는 텍스트를 단어 목록으로 분할하는 방법은 무엇입니까? (0) | 2020.09.06 |