Подтвердить что ты не робот

Добавление контроллера представления в виде подсмотра в другом контроллере представления

Я нашел несколько сообщений для этой проблемы, но ни одна из них не решила мою проблему.

Скажи, что я..

  • ViewControllerA
  • ViewControllerB

Я попытался добавить ViewControllerB в качестве подпункта в ViewControllerA, но он выдал ошибку, например "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"
}

ИЗМЕНИТЬ

С помощью предлагаемого решения от пользователей, ViewControllerB в ViewControllerA отключается от экрана. Серая рамка - это рамка, которую я создал для подсмотра. enter image description here

4b9b3361

Ответ 1

Пара наблюдений:

  1. Когда вы создаете экземпляр второго контроллера представления, вы вызываете ViewControllerB(). Если этот контроллер представления программно создает свое представление (что необычно), это было бы хорошо. Но наличие IBOutlet предполагает, что эта вторая сцена контроллера представления была определена в Интерфейсном ViewControllerB(), но, вызывая ViewControllerB(), вы не даете раскадровке возможность создать экземпляр этой сцены и подключить все выходы. Таким образом, неявно развернутый UILabel равен nil, что приводит к вашему сообщению об ошибке.

    Вместо этого вы хотите присвоить контроллеру представления назначения "идентификатор раскадровки" в Интерфейсном instantiateViewController(withIdentifier:) а затем вы можете использовать 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:)), см. Видео # 102 WWDC 2011 - Реализация содержания UIViewController. Короче говоря, вам нужно убедиться, что ваша иерархия контроллера представления синхронизируется с вашей иерархией представления, и эти вызовы addChild и didMove(toParent:) гарантируют, что это так.

    Также см. Создание пользовательских контроллеров представления контейнера в Руководстве по программированию контроллера представления.


Кстати, вышеизложенное иллюстрирует, как это сделать программно. На самом деле это намного проще, если вы используете "представление контейнера" в Интерфейсном Разработчике.

enter image description here

Тогда вам не нужно беспокоиться о вызовах, связанных с защитой, и Interface Builder позаботится об этом за вас.

Для реализации Swift 2 см. Предыдущую редакцию этого ответа.

Ответ 2

Спасибо Робу. Добавление подробного синтаксиса для вашего второго наблюдения:

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)

И для удаления диспетчера view:

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

Ответ 3

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)

Ответ 4

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

        })
    }

Ответ 5

Благодаря Робу, обновлен синтаксис 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)

Ответ 6

Также, пожалуйста, ознакомьтесь с официальной документацией по внедрению пользовательского контроллера контейнера:

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

Ответ 7

Для добавления и удаления 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()
        }

    }
}