событие отправки родительскому ViewController в swift

Я исхожу из as AS3 фона, поэтому мне может быть проще показать вам, что я пытаюсь сделать с AS3. У меня есть UIViewController(root), и внутри у меня есть ContainerView. У меня сложилось впечатление, что UIViewController представления контейнера является дочерним элементом UIViewController(root). Я хотел бы, чтобы кнопка была нажата на контроллере дочернего представления (контейнерный вид) и пузырь этого события до родительского(root UIViewController). В AS3 у меня было бы что-то вроде это

корневой класс создает дочерний класс

var childClass = new ChildClass()

childClass.addEventListener("buttonWasPressed", callThisFunction);

private function callThisFunciton(e:Event):void
{
// move the child view
TweenLite.to(childClass,1,{x:100});

}

и в дочернем классе у меня есть функция кнопки, которая развернет это событие, которое будет пузыриться до родителя.

dispatchEvent(new Event("buttonWasPressed", true));

что я не уверен, как сделать, это заставить корневой VC слушать это событие. Поскольку я использую containerView, я не уверен, как настроить выход для этого дочернего VC и слушать то, что делает ребенок. Я могу управлять перетаскиванием из IB в VC, но это только что создало выход для UIView, представляющего представление контейнера. Когда я печатаю некоторый текст, я вижу, что контроллер дочернего представления создается сначала перед родительским VC.

Я нашел этот пост, что я думаю, указывает в правильном направлении. https://craiggrummitt.wordpress.com/2014/07/14/communication-between-objects-in-objective-c-and-swift-compared-with-actionscript-part-5/

но я получаю ошибку, скорее всего, потому, что я не уверен, как сделайте соединение от родительского VC к дочернему VC, который находится внутри представления контейнера. Я огляделся, и я не могу найти много информации по этой теме.

Спасибо за помощь!

2 ответов


есть два пути:

1) Используйте протокол делегата (рекомендуется)

a) в вашем ребенке создайте протокол делегата и необязательное свойство на дочернем VC, которое будет содержать делегат.

protocol ChildViewControllerDelegate {

}

class ChildViewController: UIViewController {

    var delegate:ChildViewControllerDelegate?
}

b) в протоколе делегата создайте метод, который будет вызываться при нажатии кнопки, и реализуйте buttonWasPressed() метод в дочернем элементе, который вызывает этот метод на делегате. (Вы захотите подключить этот метод к a кнопка в раскадровке)

protocol ChildViewControllerDelegate {
    func childViewControllerDidPressButton(childViewController:ChildViewController)
}

class ChildViewController: UIViewController {

    var delegate:ChildViewControllerDelegate?

    @IBOutlet weak var button: UIButton!

    @IBAction func buttonWasPressed(sender: AnyObject) {
        self.delegate?.childViewControllerDidPressButton(self)
    }
}

c) Сделайте ваш родительский контроллер представления соответствующим дочернему протоколу

class ParentViewController: UIViewController, ChildViewControllerDelegate {

    func childViewControllerDidPressButton(childViewController: ChildViewController) {
        // Do fun handling of child button presses!
    }
}

c) Когда дочерний элемент встроен в родительский, запускается специальный вид segue, называемый embed segue. Вы можете увидеть его в раскадровке - это линия, которая соединяет ребенка к родителю:

shows the embed segue selected

добавьте идентификатор к этому сегменту в раскадровке:

shows the settings for embed segue, with the custom identifier set

и постоянном для него в контроллере родительского вида:

struct Constants {
    static let embedSegue = "embedSegue"
}

class ParentViewController: UIViewController, ChildViewControllerDelegate {

    func childViewControllerDidPressButton(childViewController: ChildViewController) {
        // Do fun handling of child button presses!
    }
}

d) затем в контроллере родительского вида переопределите prepareForSegue() метод и проверить, если segue.identifier соответствует тому, что вы установите в качестве идентификатора. Если это так, то вы можете получить ссылку на контроллер дочернего представления через segue.destinationViewController. Приведите это в качестве контроллера дочернего представления, затем вы можете установить родительский делегат:

struct Constants {
    static let embedSegue = "embedSegue"
}

class ParentViewController: UIViewController, ChildViewControllerDelegate {

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == Constants.embedSegue {
            let childViewController = segue.destinationViewController as ChildViewController
            childViewController.delegate = self
        }
    }

    func childViewControllerDidPressButton(childViewController: ChildViewController) {
        // Do fun handling of child button presses!
    }
}

e) выиграть!

2) Используйте NSNotification и NSNotificationCenter

вы можете думать об этом как о событиях ActionScript, однако они не всплывают автоматически через иерархию представлений, как AS. Вместо этого уведомления отправляются глобально через NSNotificationCenter. Я предпочитаю использовать это только тогда, когда есть несколько объектов, которые должны прослушивать одно конкретное событие.

вы можете найти более подробную информацию о NSNotification и NSNotificationCenter здесь: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/


ну или вы можете использовать статические переменные, что, вероятно, является самым простым способом.

class ParentViewController: UIViewController {

    static var instance:ParentViewController?

    override func awakeFromNib() {
        self.dynamicType.instance = self
    }

    func parentFunction() {
        print("called \(#function) in \(self.dynamicType)")
    }
}

class ChildViewController: UIViewController {
    func childFunction() {
        ParentViewController.instance?.parentFunction()
    }

    override func viewDidAppear(_ animated: Bool) {
        childFunction()
    }
}