Programing

프로그래밍 방식으로 컨테이너보기를 추가하는 방법

lottogame 2020. 9. 6. 11:51
반응형

프로그래밍 방식으로 컨테이너보기를 추가하는 방법


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)
}

이 후자의 패턴은 서로 다른 자식보기 컨트롤러간에 전환 할 때 매우 유용하며 한 자식의보기가 동일한 위치와 이전 자식보기에 있는지 확인하려는 경우 (즉, 배치에 대한 모든 고유 한 제약 조건은 컨테이너보기에 의해 결정됩니다. 매번 이러한 제약 조건을 재 구축 할 필요가 없습니다). 그러나 단순한보기 포함 만 수행하는 경우이 별도의 컨테이너보기에 대한 필요성은 덜 매력적입니다.


위의 예에서, 내가하고 있어요 translatesAutosizingMaskIntoConstraintsfalse제약 자신을 정의. 당신은 분명히 떠날 수 translatesAutosizingMaskIntoConstraintstrue하고 모두 설정 frame하고를 autosizingMask원하는 경우 표시 추가,보기에.


Swift 3Swift 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

반응형