Swift:如何将Observer设置为外部类实例,同时将选择器保留在类中?

时间:2017-10-31 01:32:16

标签: ios class swift3 uiscrollview keyboard

我需要一个外部的键盘管理器类,因为我有许多需要该服务的Viewcontrollers。我编写的代码在物理上位于Viewcontroller中时工作得很完美,但是一旦我通过Keyboard Manager类调用它,我就会得到" libc ++ abi.dylib:以NSException类型的未捕获异常终止"。

我知道这个错误通常意味着有一些与IBOutlet断开的连接,但在这种情况下我不确定这是如何相关的。

该类基本上告诉viewcontroller中的scrollview调用它向上滚动并使文本字段可见,在编辑完成时恢复状态并在点击scrollview时隐藏键盘。我也尝试使用与KeyboardManagerDelegate相同的方法,结果相同。我也尝试在任何可能的组合中将activeField,vc和scrollView变量更改为弱,但它没有改变任何内容。

这里是KeyboardManager的代码:

import UIKit

class KeyboardManager {
    var activeField: UITextField?
    var vc: UIViewController!
    var scrollView: UIScrollView!

    init(_ vc: UIViewController, _ scrollView: UIScrollView, _ activeField: UITextField?) {
        self.vc = vc
        self.scrollView = scrollView
        self.activeField = activeField
        NotificationCenter.default.addObserver(vc, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
        NotificationCenter.default.addObserver(vc, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)

        let tap = UITapGestureRecognizer(target: vc, action: #selector(hideKeyboard))
        tap.numberOfTouchesRequired = 1
        self.scrollView.addGestureRecognizer(tap)
    }

    @objc func keyboardDidShow(notification: NSNotification) {
        if let activeField = activeField, let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            print("Keyboard height is \(keyboardSize.height)")
            let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)
            scrollView.contentInset = contentInsets
            scrollView.scrollIndicatorInsets = contentInsets
            scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }

    @objc func keyboardWillHide(notification: NSNotification) {
        let contentInsets = UIEdgeInsets.zero
        scrollView.contentInset = contentInsets
        scrollView.scrollIndicatorInsets = contentInsets
    }

    @objc func hideKeyboard() {
        activeField?.resignFirstResponder()
    }

}

Viewcontroller中的相关代码在这里:

class ViewController: UIViewController, UITextFieldDelegate {

    weak var activeField: UITextField?
    var keyboardManager: KeyboardManager!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var textField: UITextField!


override func viewDidLoad() {
        super.viewDidLoad()
        keyboardManager = KeyboardManager(self, scrollView, activeField)
}

    func textFieldDidBeginEditing(_ textField: UITextField) {
        self.activeField = textField
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        self.activeField = nil
    }
}

提前感谢有关如何使这项工作的任何见解,因为我的项目现在处于暂停状态,直到我能够解决这个问题,如果我要复制和放弃,那将是太多的重复。将此代码粘贴到我拥有的每个ViewController中。

更新:我刚刚在自己的项目中发现了一些我以前没有看到的有趣内容:KeyboardManagerDrama [1731:3465043] 由于未捕获的异常终止应用程序' NSInvalidArgumentException',原因:&#39 ; - [KeyboardManagerDrama.ViewController keyboardDidShowWithNotification:]:无法识别的选择器发送到实例0x15fe18790' 所以我想也许是因为我将观察者设置为vc但是viewcontroller没有选择器功能。如果是这种情况,如何在将查看器功能保持在KeyboardManager中的同时将观察者设置为viewcontroller?

2 个答案:

答案 0 :(得分:0)

崩溃的原因是您添加了错误的通知观察者。请尝试以下代码。

NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)

let tap = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))

vc替换为self。在ViewController

您没有方法keyboardDidShowkeyboardWillHidehideKeyboard。这就是你崩溃的原因

另外,在ViewController中创建activeField时,您应该将textField替换为KeyboardManager,如下所示:

keyboardManager = KeyboardManager(self, scrollView, textField)

答案 1 :(得分:0)

代码有两个问题: 首先观察者设置在一个视图控制器上,当它应该像@trungduc那样自我设置时说:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)

let tap = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))

其次,在KeyboardManager实例中没有更新activeField属性,因此将其添加到Viewcontroller解决了100%的问题:

weak var activeField: UITextField? { 
    didSet { 
          keyboardManager.activeField = activeField 
    } 
}