Program Tip

다른 뷰 컨트롤러에서 뷰 컨트롤러를 하위 뷰로 추가

programtip 2020. 11. 22. 20:26
반응형

다른 뷰 컨트롤러에서 뷰 컨트롤러를 하위 뷰로 추가


이 문제에 대한 게시물을 거의 찾았지만 아무도 내 문제를 해결하지 못했습니다.

내가 ..

  1. ViewControllerA
  2. ViewControllerB

ViewControllerA에서 ViewControllerB를 하위보기로 추가하려고했지만 " fatal error: unexpectedly found nil while unwrapping an Optional value" 와 같은 오류가 발생 합니다.

아래는 코드입니다 ...

ViewControllerA

var testVC: ViewControllerB = ViewControllerB();

override func viewDidLoad()
{
    super.viewDidLoad()
    self.testVC.view.frame = CGRectMake(0, 0, 350, 450);
    self.view.addSubview(testVC.view);
    // Do any additional setup after loading the view.
}

ViewControllerB는 레이블이있는 단순한 화면입니다.

ViewControllerB

 @IBOutlet weak var test: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    test.text = "Success" // Throws ERROR here "fatal error: unexpectedly found nil while unwrapping an Optional value"
}

편집하다

사용자 답변의 제안 된 솔루션으로 ViewControllerA의 ViewControllerB가 화면에서 사라집니다. 회색 테두리는 하위보기 용으로 만든 프레임입니다.여기에 이미지 설명 입력


몇 가지 관찰 :

  1. 두 번째 뷰 컨트롤러를 인스턴스화 할 때 ViewControllerB(). 해당 뷰 컨트롤러가 프로그래밍 방식으로 뷰를 생성하면 (비정상적 임) 괜찮을 것입니다. 그러나의 존재는 IBOutlet이 두 번째 뷰 컨트롤러의 장면이 Interface Builder에 정의되어 있음을 시사하지만를 호출 ViewControllerB()하면 스토리 보드에 해당 장면을 인스턴스화하고 모든 콘센트를 연결할 수있는 기회를주지 않습니다. 따라서 암시 적으로 래핑되지 않은 UILabel것은 nil이므로 오류 메시지가 나타납니다.

    대신 인터페이스 빌더에서 대상 뷰 컨트롤러에 "스토리 보드 ID"를 제공하고 instantiateViewController(withIdentifier:)이를 인스턴스화하는 데 사용할 수 있습니다 (그리고 모든 IB 아웃렛을 연결). Swift 3 :

    let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
    

    이제이 액세스 할 수있는 controller'들 view.

  2. 그러나 정말로하고 싶다면 addSubview(즉, 다음 장면으로 전환하지 않는 경우) "뷰 컨트롤러 격리"라는 연습에 참여하는 것입니다. 당신은 단순히 원하는 것이 아닙니다 addSubview. 몇 가지 추가 컨테이너 뷰 컨트롤러 호출을 수행하려고합니다. 예 :

    let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
    addChild(controller)
    controller.view.frame = ...  // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
    view.addSubview(controller.view)
    controller.didMove(toParent: self)
    

    더이는 이유에 대한 자세한 내용은 addChild(이전에 호출 addChildViewController) 및 didMove(toParent:)(이전이라고 didMove(toParentViewController:)) 필요, 참조 구현의 UIViewController 봉쇄 - WWDC 2011 비디오 # 102 . 요컨대, 뷰 컨트롤러 계층이 뷰 계층과 동기화되어 있는지 확인해야 하며 이러한 호출이 addChild이에 해당 didMove(toParent:)하는지 확인해야합니다.

    View Controller Programming GuideCreating Custom Container View Controllers 도 참조하십시오 .


그런데 위의 내용은 프로그래밍 방식으로 수행하는 방법을 보여줍니다. Interface Builder에서 "컨테이너 뷰"를 사용하면 실제로 훨씬 더 쉽습니다.

여기에 이미지 설명 입력

그러면 이러한 포함 관련 호출에 대해 걱정할 필요가 없으며 Interface Builder가 처리해줍니다.

Swift 2 구현 의 경우이 답변의 이전 개정판을 참조하십시오 .


Rob에게 감사합니다. 두 번째 관찰에 대한 자세한 구문 추가 :

let controller:MyView = self.storyboard!.instantiateViewControllerWithIdentifier("MyView") as! MyView
controller.ANYPROPERTY=THEVALUE // If you want to pass value
controller.view.frame = self.view.bounds;
controller.willMoveToParentViewController(self)
self.view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)

그리고 viewcontroller를 제거하려면 :

self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController() 

This code will work for Swift 4.2.

let controller:SecondViewController = 
self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as! 
SecondViewController
controller.view.frame = self.view.bounds;
controller.willMove(toParent: self)
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)

func callForMenuView () {

    if(!isOpen)

    {
        isOpen = true

        let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "menu") as! MenuViewController
        self.view.addSubview(menuVC.view)
        self.addChildViewController(menuVC)
        menuVC.view.layoutIfNeeded()

        menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height);

        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height);
    }, completion:nil)

    }else if(isOpen)
    {
        isOpen = false
      let viewMenuBack : UIView = view.subviews.last!

        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            var frameMenu : CGRect = viewMenuBack.frame
            frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width
            viewMenuBack.frame = frameMenu
            viewMenuBack.layoutIfNeeded()
            viewMenuBack.backgroundColor = UIColor.clear
        }, completion: { (finished) -> Void in
            viewMenuBack.removeFromSuperview()

        })
    }

Rob 덕분에 Swift 4.2 구문 업데이트

let controller:WalletView = self.storyboard!.instantiateViewController(withIdentifier: "MyView") as! WalletView
            controller.view.frame = self.view.bounds;
            controller.willMove(toParent: self)
            self.view.addSubview(controller.view)
            self.addChild(controller)
            controller.didMove(toParent: self)

커스텀 컨테이너 뷰 컨트롤러 구현에 대한 공식 문서도 확인하세요.

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1

이 문서는 모든 지침에 대해 훨씬 더 자세한 정보를 제공하며 전환을 추가하는 방법도 설명합니다.

Swift 3로 번역되었습니다.

func cycleFromViewController(oldVC: UIViewController,
               newVC: UIViewController) {
   // Prepare the two view controllers for the change.
   oldVC.willMove(toParentViewController: nil)
   addChildViewController(newVC)

   // Get the start frame of the new view controller and the end frame
   // for the old view controller. Both rectangles are offscreen.r
   newVC.view.frame = view.frame.offsetBy(dx: view.frame.width, dy: 0)
   let endFrame = view.frame.offsetBy(dx: -view.frame.width, dy: 0)

   // Queue up the transition animation.
   self.transition(from: oldVC, to: newVC, duration: 0.25, animations: { 
        newVC.view.frame = oldVC.view.frame
        oldVC.view.frame = endFrame
    }) { (_: Bool) in
        oldVC.removeFromParentViewController()
        newVC.didMove(toParentViewController: self)
    }
}

ViewController 추가 및 제거

 var secondViewController :SecondViewController?

  // Adding 
 func add_ViewController() {
    let controller  = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController")as! SecondViewController
    controller.view.frame = self.view.bounds;
    controller.willMove(toParent: self)
    self.view.addSubview(controller.view)
    self.addChild(controller)
    controller.didMove(toParent: self)
    self.secondViewController = controller
}

// Removing
func remove_ViewController(secondViewController:SecondViewController?) {
    if secondViewController != nil {
        if self.view.subviews.contains(secondViewController!.view) {
             secondViewController!.view.removeFromSuperview()
        }

    }
}

참고 URL : https://stackoverflow.com/questions/27276561/adding-a-view-controller-as-a-subview-in-another-view-controller

반응형