Swift3 Вызов функции у родителя вне функции в дочернем представлении

У меня есть дочерний класс UIView, созданный в UIViewController, UIViewController имеет функцию с именем sigIn(){}. Мне нужно вызвать эту функцию изнутри UIView.

Я попытался как-то использовать self.superview и привести его в UIViewController, но это не сработало. Я нашел метод, в котором вы используете слушателя для обнаружения триггера, но я думаю, что это не очень хорошее решение. Есть ли другой простой способ доступа к функциям в представлении paren из подпредставления?

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

    ("Call parent signIn() function")

  }

  (init etc. ...)
}

2 ответа

Решение

Во-первых, View и View Controller - это две разные концепции. .superview не даст вам View Controller, а приведение к нему приведет только к краху программы.

Несмотря на то, что можно узнать текущий View Controller, он очень однотипен в вашем случае использования, потому что вы не можете быть уверены, что View Controller имеет signIn() функции, и вы даже не можете гарантировать, что "текущий контроллер представления" является контроллером представления для представления.


iOS обычно использует вместо этого "шаблон делегата". В вашем случае сначала вы определяете протокол для signIn() функция:

protocol SignInDelegate {
    func signIn()
}

Затем View должен предоставить переменную этого протокола. Эта переменная называется делегатом. Всякий раз, когда мы хотим войти, просто позвоните делегату signIn() функция.

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

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

Далее мы адаптируем протокол к View Controller:

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

Обычно мы предоставляем этот делегат Интерфейсному Разработчику, чтобы мы могли просто подключить View к View Controller, чтобы назначить делегата.

Это делается путем добавления @IBOutlet в поле делегата. К сожалению, Xcode распознает только AnyObject / NSObject для IBOutlet, который побеждает всю цель использования шаблона делегата для безопасности типов. Поэтому нам нужно ввести некрасивый хак, чтобы обойти это для 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 https://stackru.com/a/42227800/224671
    #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.
}

Объявить протокол для signIn() функция и пусть родительский ViewController соответствует этому протоколу. Затем сделайте родительский 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. ...)
}
Другие вопросы по тегам