iOS:在 Swift 中重新创建 addTarget() 的最佳实践?

Posted

技术标签:

【中文标题】iOS:在 Swift 中重新创建 addTarget() 的最佳实践?【英文标题】:iOS: Best practice for recreating addTarget() in Swift? 【发布时间】:2018-01-18 18:55:21 【问题描述】:

我正在创建一个自定义UIControl 子类,并想添加一个addTarget() 函数,类似于UIButton。我该如何正确地做到这一点?到目前为止,我有这个(它有效,但不确定是否是一个好的解决方案):

import UIKit

class RichLabel: UIControl 

    var labelTarget: Any?
    var labelAction: Selector?

    override init(frame: CGRect) 
        super.init(frame: frame)
    

    required init?(coder _: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    func addTarget(_ target: Any?, action: Selector) 
        self.labelTarget = target
        self.labelAction = action
    

    func buttonAction() 

        if let labelTarget = labelTarget, let labelAction = labelAction                
         let _ = (labelTarget as AnyObject).perform(labelAction, with: self)
    

目标是从另一个视图添加的,如下所示:

var richLabel: RichLabel 
    let label = RichLabel()
    label.setAttributedText(title: string)
    label.addTarget(self, action: #selector(richLabelAction(label:)))
    return label

【问题讨论】:

UIControl 已经有func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControlEvents) 可用 - 为什么不直接使用它呢?您只需选择与触发Selector 所需的操作相匹配的UIControlEvent 所以我应该重写那个函数?或者如何触发使用包含的 UIControl addTarget 函数设置的选择器? 这完全取决于目标是谁。这是RichLabel 在用户与之交互时会做出反应并做某事的事情,还是包含UIViewController 需要知道的事情? 目标是另一个视图或其他什么。它应该像通常使用的 UIButton 的 addTarget 一样使用。更新了我的问题。 检查我的答案 - 这将完成我认为你所要求的。与我处理按钮点击的方式完全相同。 【参考方案1】:

如果您从UIControl 继承,则无需对addTarget 执行任何操作。调用者将在您的自定义 UIControl 上使用addTarget,就像使用任何其他控件一样。您的自定义控件所要做的就是决定何时调用这些操作,然后调用sendActions(for:)

如果您的目标是让这个RichLabel 类函数像一个按钮一样,我会在其中添加一个UITapGestureRecognizer,然后在手势识别器中调用self.sendActions(for: .touchUpInside)

class RichLabel: UIControl 

    override init(frame: CGRect) 
        super.init(frame: frame)
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
        addGestureRecognizer(tapGesture)
    

    required init?(coder _: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    @objc func labelTapped() 
        // When this happens, any object that called `addTarget`
        // for the .touchUpInside event will get its callback triggered
        sendActions(for: .touchUpInside)
    


【讨论】:

谢谢。这就是我一直在寻找的。 :) 我可能需要为许多不同的事件添加目标,所以我可能需要做一个自定义的,所以我可以拥有与UIControlState 提供的不同/更多的状态。你知道我应该如何最好地存储目标/选择器吗? UIControl 如何在内部做到这一点? 存储动作/选择器是UIControl 的一个实现细节,我认为我们作为开发人员不需要了解。您确定不能使用UIControlState 提供的值吗?请记住,您可以在选择器调用的函数中传回RichLabel。所以如果你有一个#selector(labelTapped(_:)),你的函数看起来像func labelTapped(_ label: RichLabel)。因此,您可以使用label 来获取有关上下文的更多信息,而不是一堆不同的选择器。 好的,谢谢。我会尝试通过这种方式解决它。 :) 这不是真的:“您的自定义控件所要做的就是决定何时要调用这些操作..”。这不是必需的,我会说这是一种不好的做法。将 TapGR 添加到 UIControl 类是不必要的,并且从那里发送操作会令人困惑。谁会期望 UIControl 的 TouchUpInside 事件会立即被调用?没有机会取消触摸(通过 TouchUpOutside)等。【参考方案2】:

由于UIControl 已经允许您访问func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControlEvents),您可以使用它。例如,如果您将标签添加到 UIViewController,并希望该 viewController 能够对 editingDidBegin 之类的内容做出反应,您可以将此代码添加到您的 viewController:

@IBOutletWeak var richLabel: RichLabel!

override func viewDidLoad() 
    super.viewDidLoad()
    self.richLabel.addTarget(self, action: #selector(labelEditingDidBegin(_:)), for: UIControlEvents.editingDidBegin)


@objc func labelEditingDidBegin(_ sender: RichLabel) 
    print(sender.text)

我不确定您的RichLabel 是否允许编辑等,但您可以观察您需要的任何UIControlEvent,对其做出反应,然后在您的Selector 函数中执行您需要的任何代码。

【讨论】:

@Connor 啊,好的,我从问题中不明白这一点。不错 - 你得到了我的支持。

以上是关于iOS:在 Swift 中重新创建 addTarget() 的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

重新加载uitableviewcell,iOs / Swift时随机滚动

为啥网络库无法检测到何时在 Swift Ios 中重新连接了互联网

wkwebview ios 10在重新创建webview后停止使用localfileurl加载文件

重新编辑:音频不在真正的 ios 设备中播放,而是在 Swift2 的模拟设备中播放

在 Swift 中应用混合模式

界面生成器中的 iOS Swift 3.0 自定义视图导致重新编译和错位