在委托方法中使用默认行为对 UITextField 进行子类化的设计方法
Design Approach for Subclassing UITextField with default behaviors in delegate methods
我已经子class编辑了 UITextField class,这样我就可以为我的应用程序提供一些内置功能。我更改了外观,只为 UX 设计提供下划线边框。此外,我想在有选择器(选择列表、日期/时间选择器)的情况下使用此控件。在这些情况下,我想阻止编辑,但我仍然需要响应触摸事件。为此,我添加了可检查属性以从 IB 对其进行控制。
我可以通过这样做轻松地阻止 copy/paste 菜单出现:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if isFirstResponder && disableEditing {
DispatchQueue.main.async(execute: {
(sender as? UIMenuController)?.setMenuVisible(false, animated: false)
})
return false
}
return super.canPerformAction(action, withSender: sender)
}
但是,我需要防止他们在选取器中选择某些内容后在文本字段中键入或删除字符。通常,您会使用以下委托方法:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return false
}
问题是如何在子class中提供这种默认行为?你可以做一个:
self.delegate = self
然而,这会导致各种缺点,因此不是一个好的解决方案。
另一个解决方案是实现一个基本的 UIViewController subclass (MyBaseViewController),但这会导致以后的代码复杂和单一。
如果有一种简洁的方式以封装的方式提供这种默认行为,那将是最好的。
显然,还有许多其他方法可以解决这个问题(即在 10 个视图控制器中编写相同的代码)。从本质上讲,当像这样子classing 控件时似乎应该有重用委托代码的方法。
大家有什么想法吗??
你将采取的每一种方法都是一种权衡。我认为没有针对此类问题的完美解决方案。在我看来,最好的解决方案是将您的自定义 UITextField
实现为一种代理委托。
您可以通过两种方式进行。这是最简单的。
class CustomTextField: UITextField, UITextFieldDelegate
{
var externalDelegate: UITextFieldDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
externalDelegate?.textFieldDidBeginEditing?(textField)
}
}
如果你不想修改自定义控件的委托接口,你可以做一个小技巧,覆盖 delegate
属性.
override var delegate: UITextFieldDelegate? {
didSet {
if delegate === self {
return
}
externalDelegate = delegate
delegate = oldValue
}
}
优点
- 从代码和情节提要配置时效果很好。
- 从客户端代码的角度来看,它是透明的。
缺点
这种方法的缺点是您必须在 UITextField
子类中实现 UITextFieldDelegate
协议中的每个方法以完全支持委托。幸运的是,只有 8 个,您不太可能需要全部,因此您可以将其缩小到所需的子集。
我已经子class编辑了 UITextField class,这样我就可以为我的应用程序提供一些内置功能。我更改了外观,只为 UX 设计提供下划线边框。此外,我想在有选择器(选择列表、日期/时间选择器)的情况下使用此控件。在这些情况下,我想阻止编辑,但我仍然需要响应触摸事件。为此,我添加了可检查属性以从 IB 对其进行控制。
我可以通过这样做轻松地阻止 copy/paste 菜单出现:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if isFirstResponder && disableEditing {
DispatchQueue.main.async(execute: {
(sender as? UIMenuController)?.setMenuVisible(false, animated: false)
})
return false
}
return super.canPerformAction(action, withSender: sender)
}
但是,我需要防止他们在选取器中选择某些内容后在文本字段中键入或删除字符。通常,您会使用以下委托方法:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return false
}
问题是如何在子class中提供这种默认行为?你可以做一个:
self.delegate = self
然而,这会导致各种缺点,因此不是一个好的解决方案。
另一个解决方案是实现一个基本的 UIViewController subclass (MyBaseViewController),但这会导致以后的代码复杂和单一。
如果有一种简洁的方式以封装的方式提供这种默认行为,那将是最好的。
显然,还有许多其他方法可以解决这个问题(即在 10 个视图控制器中编写相同的代码)。从本质上讲,当像这样子classing 控件时似乎应该有重用委托代码的方法。
大家有什么想法吗??
你将采取的每一种方法都是一种权衡。我认为没有针对此类问题的完美解决方案。在我看来,最好的解决方案是将您的自定义 UITextField
实现为一种代理委托。
您可以通过两种方式进行。这是最简单的。
class CustomTextField: UITextField, UITextFieldDelegate
{
var externalDelegate: UITextFieldDelegate?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
delegate = self
}
func textFieldDidBeginEditing(_ textField: UITextField) {
externalDelegate?.textFieldDidBeginEditing?(textField)
}
}
如果你不想修改自定义控件的委托接口,你可以做一个小技巧,覆盖 delegate
属性.
override var delegate: UITextFieldDelegate? {
didSet {
if delegate === self {
return
}
externalDelegate = delegate
delegate = oldValue
}
}
优点
- 从代码和情节提要配置时效果很好。
- 从客户端代码的角度来看,它是透明的。
缺点
这种方法的缺点是您必须在 UITextField
子类中实现 UITextFieldDelegate
协议中的每个方法以完全支持委托。幸运的是,只有 8 个,您不太可能需要全部,因此您可以将其缩小到所需的子集。