Swift3 在​​子视图中的父函数外调用函数

Swift3 Call function in parent out of function in child view

我有一个在 UIViewController 中实例化的子 UIView class,UIViewController 有一个名为 sigIn(){} 的函数。我需要从 UIView 中调用这个函数。

我尝试以某种方式使用 self.superview 并将其转换为 UIViewController 但这没有用。我找到了一种使用侦听器检测触发​​器的方法,但我认为这不是一个好的解决方案。是否有另一种简单的方法可以从子视图访问父视图中的函数?

class SignUIView: UIView {
  func signInUIButtonH(sender: UIButton) {

    ("Call parent signIn() function")

  }

  (init etc. ...)
}

signIn() 函数声明一个协议,并让父 [​​=15=] 遵守该协议。然后让父 ViewController 成为 SignUIView 的委托:

protocol SignInProtocol {
    func signIn()
}

class MyViewController: UIViewController, SignInProtocol {
    var signUIView: SignUIView

    override func viewDidLoad() {
        super.viewDidLoad()

        self.signUIView = SignUIView()
        self.signUIView.delegate = self  // set the delegate
    }


    func signIn() {
        ...
    }
}


class SignUIView: UIView {
  var delegate: SignProtocol?   // the delegate

  func signInUIButtonH(sender: UIButton) {
    // call the delegate
    self.delegate?.signIn()
  }

  (init etc. ...)
}

首先,.superview 不会给你一个 View Controller,转换它只会使程序崩溃。

虽然 it is possible to find out the current View Controller,但它在您的用例中非常不合常理,因为您无法确定 View Controller 是否具有 signIn() 功能,您甚至无法确保 "current View Controller"是视图的视图控制器。


iOS 通常使用 代替。在您的情况下,首先为 signIn() 函数定义一个 协议

protocol SignInDelegate {
    func signIn()
}

那么View需要提供一个这个协议的变量。此变量称为 delegate。每当我们想登录时,只需调用委托的 signIn() 函数即可。

变量应该是对. The protocol also 的弱引用,否则编译器会报错。

protocol SignInDelegate: class {    // <-- needs :class for weak
    func signIn()
}

class SignUIView: UIView {
    weak var delegate: SignInDelegate?    // <-- delegate

    func signInUIButtonH(sender: UIButton) {
        delegate?.signIn()    // <-- call the delegate
    }
}

接下来我们调整协议以适应视图控制器:

class SignInViewController: UIViewController, SignInDelegate {   // <-- adapt the protocol
    func signIn() {    // <-- implement the function
        print("sign in")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        signInView.delegate = self    // <-- tell the subview we will be the delegate.
    }
}

我们通常将此委托公开给 Interface Builder,因此我们可以简单地将视图连接到视图控制器以分配委托。

这是由 adding @IBOutlet to the delegate field. Unfortunately, Xcode only recognizes AnyObject/NSObject for IBOutlet 完成的,这违背了使用委托模式实现类型安全的全部目的。所以我们需要引入一些丑陋的 hack 来为 IB 解决它。该协议现在还需要 @objc,因为解析这些 IB 连接需要 Objective-C 运行时。

@objc protocol SignInDelegate {   // <-- needs @objc for IB (:class is implied by @objc)
    func signIn()
}

class SignUIView {
    // Blame Xcode for this mess. See 
    #if TARGET_INTERFACE_BUILDER
    @IBOutlet weak var delegate: AnyObject?
    #else
    weak var delegate: SignInDelegate?
    #endif

    func signInUIButtonH(sender: UIButton) {
        delegate?.signIn()
    }
}

class SignInViewController: UIViewController, SignInDelegate {   // <-- adapt the protocol
    func signIn() {    // <-- implement the function
        print("sign in")
    }
    // Note: no need to set signInView.delegate manually.
}