是否可以在 Swift 中进行暴露给 Objective-C 的闭包?为界面元素制作目标是否值得?[复制]

Is it possible in Swift to make a closure that is exposed to Objective-C? And is it worth it for making targets for interface elements? [duplicate]

提问人:Afinainflowers 提问时间:1/5/2023 更新时间:1/5/2023 访问量:278

问:

为了提供更多上下文:我的应用程序中的屏幕有一个表格,一些表格单元格中有开关 (UISwitch)。

我想在创建每个开关的那一刻给它一个目标,如下所示:

cell.switch.addTarget(self, action: #selector({// do stuff }), for: .valueChanged)

我希望将函数制作成一个内联的选择器,因为它应该略有不同(更改数组中的不同值),具体取决于开关被翻转的行数。

问题是 - 上面的块没有编译,因为闭包没有暴露给 objective-C。我可以以某种方式在同一行中公开它吗?

我确实知道标记我的开关并为它们制作共享调用函数的选项,该函数的行为因标签而异,如下所示:

cell.switch.tag = switchNumber
cell.switch.addTarget(self, action: #selector(switchFlipped), for: .valueChanged)
@objc func switchFlipped(sender: UISwitch){
        let switchNumber = sender.tag
        switch(tag){
            // do stuff depending on the case
        }
    }

但我觉得可能进行内联闭合会更快。我错了吗?

iOS Swift UIKIT 闭包 UISswitch

评论

0赞 Rob Napier 1/5/2023
这里的问题与闭包是否暴露给 ObjC 无关。问题在于,这不是选择器的工作方式。请参阅被欺骗的问题,了解正在发生的事情的解释,以及各种选项。关键是选择器不是一个函数。它是要传递给 ObjC 对象的消息的名称。通常,该消息将转换为同名的方法调用。虽然选择器现在是编译器可以理解的类型,但它们在历史上一直只是字符串(从概念上讲,它们仍然非常像字符串)。
0赞 rob mayoff 1/5/2023
如果您的部署目标是 iOS 14 或更高版本,请创建一个 来包装您的闭包,并在交换机上使用该方法而不是该方法。UIActionaddActionaddTarget

答:

2赞 HangarRash 1/5/2023 #1

简而言之,不,您不能将 Swift 闭包从 传递给 API。addTarget/selectorUIControl

您应该做的是将所有这些详细信息隐藏在自定义表视图单元格类的实现中。数据源方法甚至不应该知道单元格有开关。cellForRowAt

自定义单元格类应使自身成为开关操作的处理程序。自定义单元格类应提供回调闭包属性,当开关的值发生更改时,它会调用该属性。在 ur 中,您可以为此属性分配一个闭包。valueChangedcellForRowAt

此设置将单元格特征的详细信息保留在单元格类中。这也使您的实现更加简洁,并且允许您使用闭包来处理自定义单元可能触发的任何事件。cellForRowAt

下面是自定义单元格类的粗略示例:

class MyCustomCell: UITableViewCell {
    var actionHandler: ((MyCustomCell, Bool) -> Void)?

    private var aSwitch: UISwitch!

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        commonInit()
    }

    private func commonInit() {
        // setup switch in storyboard or here in code as needed

        aSwitch.addTarget(self, action: #selector(switchChanged), for: .valueChanged)
    }

    @objc func switchChanged(_ sender: UISwitch) {
        actionHandler?(self, sender.isOn)
    }
}

而你的:cellForRowAt

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "someIdentifier", for: indexPath) as! MyCustomCell

    cell.actionHandler = { (cell, state) in
        // Do stuff
    }

    return cell
}

根据设置的需要调整细节。