Swift: переключение между NSViewController внутри Container View/NSView

Я хочу выполнить очень простую задачу — изменить ViewController представления контейнера нажатием кнопки:

введите описание изображения здесь

В моем примере ViewController1 встроен в представление контейнера с помощью построителя интерфейсов. Нажав кнопку ViewController2, я хочу изменить вид на второй ViewController.

Я в замешательстве, потому что сам Container View кажется NSView, если я создаю Outlet, и, насколько мне известно, NSView не может содержать VC. Очень ценю вашу помощь!


person ixany    schedule 24.11.2016    source источник


Ответы (2)


Просто обратите внимание, что для того, чтобы это работало, вы должны добавить идентификаторы раскадровки в свои контроллеры представления, что можно сделать, перейдя в свою раскадровку, затем выбрав Инспектор удостоверений на правой панели, а затем введя Storyboard ID в подкатегорию Identity.

Тогда эта реализация ViewController достигнет того, что вы ищете.

import Cocoa

class ViewController: NSViewController {

    // link to the NSView Container
    @IBOutlet weak var container : NSView!

    var vc1 : ViewController1!
    var vc2 : ViewController2!

    var vc1Active : Bool = false

    override func viewDidLoad() {
        super.viewDidLoad()

        // Make sure to set your storyboard identiefiers on ViewController1 and ViewController2
        vc1 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController1") as! ViewController1
        vc2 = NSStoryboard(name: "name", bundle: nil).instantiateController(withIdentifier: "ViewController2") as! ViewController2

        self.addChild(vc1)
        self.addChild(vc2)
        vc1.view.frame = self.container.bounds
        self.container.addSubview(vc1.view)
        vc1Active = true

    }

    // You can link this action to both buttons
    @IBAction func switchViews(sender: NSButton) {

        for sView in self.container.subviews {
            sView.removeFromSuperview()
        }

        if vc1Active == true {

            vc1Active = false
            vc2.view.frame = self.container.bounds
            self.container.addSubview(vc2.view)

        } else {

            vc1Active = true
            vc1.view.frame = self.container.bounds
            self.container.addSubview(vc1.view)
        }

    }
}
person Wes    schedule 24.11.2016
comment
Спасибо @Wes за подробный ответ! У меня ошибка в строке self.container.addSubview(vc1.view): «В представлении контейнера есть неожиданные подпредставления». Итак, я попытался удалить все подпредставления (как вы сначала сделали в своем func switchViews, но это не сработало (ошибка остается той же). Есть идеи? - person ixany; 25.11.2016
comment
@ixany пару вещей. Во-первых, измените свой код, чтобы отразить сделанные мной изменения (незначительные изменения, чтобы избежать путаницы в представлении). Кроме того, удалите взаимосвязь внедрения между Container и ViewController1. Сделайте это, щелкнув правой кнопкой мыши Container в IB и удалив соединение. - person Wes; 25.11.2016
comment
Еще раз спасибо @Wes! Вы были правы, мне пришлось удалить вставку/переход к ViewController1 в IB. Я принял ваш ответ, но у меня есть еще один вопрос: является ли добавление и удаление подвидов для этой задачи лучшей практикой? При его реализации я также нашел .removeChildViewController() и подумал, что он может быть лучше с точки зрения производительности. В subview-решении приложение загружает все возможные VC, даже если они никогда не появятся. Есть ли другие преимущества добавления/удаления подвидов? - person ixany; 27.11.2016
comment
Таким образом, все уже рассчитано, и ресурсы загружаются до того, как они вам понадобятся, поэтому в последнюю секунду не выполняется расчет/загрузка ресурсов. При этом вы правы, если вам никогда не понадобится определенный vc, это не лучший подход, и было бы лучше лениво объявлять их при загрузке только тогда, когда это необходимо, а затем сносить их. Однако, если вы уверены, что вам понадобятся эти контроллеры, нагрузка на производительность при сохранении ссылки на два контроллера довольно мала, а предварительная загрузка может немного увеличить производительность. - person Wes; 28.11.2016
comment
@Wes @ixany Я знаю, что это было давно, но мне нравится это решение за то, что я делаю, но я продолжаю получать следующую ошибку, и мне интересно, знаете ли вы, как ее решить: Ambiguous use of 'instantiateController(identifier:creator:)'. Затем, когда я нажимаю на нее, найдены два кандидата, и оба AppKit.NSStoryBoard - person Kyra; 31.05.2021

возможно, это поздний ответ, но я все равно опубликую свое решение. Надеюсь, это поможет кому-то.

Я встроил NSTabViewController в ContainerView. Затем, чтобы не видеть вкладки сверху, я сделал так:

  • перейдите к NSTabViewController в раскадровке

  • в инспекторе атрибутов изменить стиль на Unspecified

  • затем нажмите TabView в контроллере представления панели вкладок и установите стиль «без таблиц»:

    введите здесь описание изображения

После этого вам необходимо:

  • сохранить ссылку tabViewController на mainViewController, чтобы выбрать вкладки из кода
  • добавьте кнопку в mainViewController (там, где находится ваш контейнер), с помощью которой вы будете менять вкладки в tabViewController.

Вы делаете это, сохраняя ссылку на tabViewController при переопределении функции подготовки к переходу. Вот мой код:

сначала добавьте свойство в mainViewController

private weak var tabViewController: NSTabViewController?

затем переопределите эту функцию и сохраните ссылку на tabViewController:

    override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
            guard let tabViewController = segue.destinationController
                as? NSTabViewController else { return }

            **self.tabViewController = tabViewController as? NSTabViewController**

        }

После этого у вас будет ссылка на все настройки tabViewController. Следующее (последнее), что вам нужно сделать, это сделать действие для кнопки, чтобы перейти к первому (или второму) контроллеру представления, например:

@IBAction func changeToSecondTab(_ sender: Any) {
        self.tabViewController?.selectedTabViewItemIndex = 0 // or 1 for second VC 
    } 

Всего наилучшего!

person Damir    schedule 20.12.2017