【问题标题】:dispatch event to parent ViewController in swift快速向父 ViewController 分派事件
【发布时间】:2015-05-31 09:17:13
【问题描述】:

我来自 AS3 背景,因此我可能更容易向您展示我正在尝试使用 AS3 做什么。我有一个 UIViewController(root),里面有一个 ContainerView。我的印象是容器视图的 UIViewController 是 UIViewController(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 创建了一个出口。当我 println 一些文本时,我可以看到子视图控制器在父 VC 之前首先被实例化。

我发现这篇文章指出了正确的方向。 https://craiggrummitt.wordpress.com/2014/07/14/communication-between-objects-in-objective-c-and-swift-compared-with-actionscript-part-5/

但是我遇到了一个错误,很可能是因为我不确定如何建立从父 VC 到容器视图内的子 VC 的连接。我环顾四周,似乎找不到关于该主题的太多信息。

感谢您的帮助!

【问题讨论】:

    标签: xcode actionscript-3 swift uiview uicontainerview


    【解决方案1】:

    有两种方式:

    1) 使用委托协议(推荐)

    a) 在您的孩子中,创建一个委托协议,并在将持有委托的子 VC 上创建一个可选属性。

    protocol ChildViewControllerDelegate {
    
    }
    
    class ChildViewController: UIViewController {
    
        var delegate:ChildViewControllerDelegate?
    }
    

    b) 在委托协议中创建一个在按钮被按下时将被调用的方法,并在委托上调用该方法的子进程中实现buttonWasPressed() 方法。 (您需要将此方法与情节提要中的按钮连接起来)

    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。您可以在情节提要中看到它——它是连接孩子和父母的线:

    在情节提要中为该 segue 添加一个标识符:

    并在父视图控制器中为其设置一个常量:

    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 全局发送的。我更喜欢只在有多个对象需要监听一个特定事件时才使用它。

    您可以在此处找到有关NSNotificationNSNotificationCenter 的更多信息:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/

    【讨论】:

    • 感谢您非常详尽的回答!
    • 如果子视图以编程方式实现怎么办? segue 是如何工作的?
    • 对于这个解决方案,我的 func childViewControllerDidPressButton(childViewController: ChildViewController) { // 对子按钮按下进行有趣的处理! } 从来没有收到任何东西。有什么变化吗?
    【解决方案2】:

    或者你可以使用静态变量,这可能是最直接的方式。

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

    【讨论】:

    • 这是一个好的解决方案吗?以编程方式会由此引发什么问题(如果有的话?)
    • 这在几个方面通常是个坏主意。首先,您将 ChildViewController 耦合到 ParentViewController,没有其他人可以使用 ChildViewController。其次,如果您有两个 ParentViewController 实例,这将不再有效,因为只有一个静态实例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-07
    • 2014-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多