在我的项目中,我很少有视图控制器,它们是uiTableViewController、uiViewController的子类,在每个我想要实现此行为的对象上:
当用户点击文本字段外时,应关闭当用户点击文本字段内时可见的键盘。
我可以通过定义tap笔势识别器并关联选择器来消除键盘,从而轻松实现它:
class MyViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureToDismissKeyboard()
}
private func configureToDismissKeyboard() {
let tapGesture = UITapGestureRecognizer(target: self, action: "hideKeyboard")
tapGesture.cancelsTouchesInView = true
form.addGestureRecognizer(tapGesture)
}
func hideKeyboard() {
form.endEditing(true)
}
}
因为我必须在多个视图控制器中实现相同的行为,所以我试图找出一种避免在多个类中使用重复代码的方法。
我的一个选择是定义一个
BaseViewController
子类,它是UIViewController
的子类,在其中定义了所有上述方法,然后将每个视图控制器子类化为BaseViewController
。这种方法的问题在于我需要定义两个BaseViewController
s,一个用于UIViewController
一个用于UITableViewController
因为我使用的是这两个子类。我尝试使用的另一个选项是-
Protocol-Oriented Programming
。所以我定义了一个协议:protocol DismissKeyboardOnOutsideTap {
var backgroundView: UIView! { get }
func configureToDismissKeyboard()
func hideKeyboard()
}
然后定义其扩展名:
extension DismissKeyboardOnOutsideTap {
func configureToDismissKeyboard() {
if let this = self as? AnyObject {
let tapGesture = UITapGestureRecognizer(target: this, action: "hideKeyboard")
tapGesture.cancelsTouchesInView = true
backgroundView.addGestureRecognizer(tapGesture)
}
}
func hideKeyboard() {
backgroundView.endEditing(true)
}
}
在我的视图控制器中,我确认了协议:
class MyViewController: UITableViewController, DismissKeyboardOnOutsideTap {
var backgroundView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// configuring background view to dismiss keyboard on outside tap
backgroundView = self.tableView
configureToDismissKeyboard()
}
}
问题是-以上代码崩溃,异常:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyProject.MyViewController hideKeyboard]: unrecognized selector sent to instance 0x7f88c1e5d700'
为了避免这种崩溃,我需要在
hideKeyboard
类中重新定义MyViewController
函数,这违背了我避免重复代码的目的:(如果我在这里做了什么错事,或者有更好的方法来实现我的要求,请提出建议。
最佳答案
我认为有两个可能的问题:将Self
强制转换为AnyObject
,而不使用新的#selector
语法。
不是将Self
强制转换为AnyObject
,而是将协议定义为仅类协议:
protocol DismissKeyboardOnOutsideTap: class {
// protocol definitions...
}
然后使用类型约束将扩展仅应用于uiviewController的子类,并直接在代码中使用
Self
,而不是强制转换为AnyObject
:extension DismissKeyboardOnOutsideTap where Self: UIViewController {
func configureToDismissKeyboard() {
let gesture = UITapGestureRecognizer(target: self,
action: #selector(Self.hideKeyboard()))
gesture.cancelsTouchesInView = true
backgroundView.addGestureRecognizer(gesture)
}
}
编辑:我记得我做这件事时遇到的另一个问题。
action
的参数是一个objective-c选择器,但是类的swift扩展不是objective-c。因此我将协议更改为一个UITapGestureRecognizer
协议,但这是一个问题,因为我的协议包含了一些swift选项,并且在我尝试实现protoc时它也引入了新的崩溃。在我的风投里。最后,我发现了一个不需要使用Objective-C选择器作为参数的替代方法;在我的例子中,我设置了一个NSNotificationCenter观察者。
在您的情况下,您最好简单地扩展
@objc
,因为UIViewController
是一个子类,而子类继承扩展(我认为)。