向使用 UIViewRepresentable 创建的按钮添加操作

Add action to button created using UIViewRepresentable

我正在使用 FluentUI#button,它后面使用了 UIKit

我需要在 SwiftUI 视图中显示该按钮,我正在尝试切换 @State 属性 或向按钮添加 #selector,但我做不到

我创建了一个通用的 UIViewRepresentable 结构来帮助我在我的 SwiftUI 视图中嵌入任何 UIView,遵循这个 tutorial:

struct Anything<Wrapper : UIView>: UIViewRepresentable {
    typealias Updater = (Wrapper, Context) -> Void

    var makeView: () -> Wrapper
    var update: (Wrapper, Context) -> Void

    init(_ makeView: @escaping @autoclosure () -> Wrapper,
         updater update: @escaping (Wrapper) -> Void) {
        self.makeView = makeView
        self.update = { view, _ in update(view) }
    }

    func makeUIView(context: Context) -> Wrapper {
        makeView()
    }

    func updateUIView(_ view: Wrapper, context: Context) {
        update(view, context)
    }
}

我有以下代码:

import SwiftUI
import FluentUI

struct MyView: View {
    @State var isGreen = true
    
    var body: some View {
        VStack {
            Text("Hello, World!")
                .background(isGreen ? Color.green : Color.blue)
            Spacer().frame(height: 20)
            Anything(FluentUI.Button(style: .primaryFilled)) {
                [=11=].setTitle("Try me!", for: .normal)
            }
            .frame(height: 30)
            .padding()
        }
    }
}

struct Anything<Wrapper: UIView>: UIViewRepresentable {
    typealias Updater = (Wrapper, Context) -> Void

    var makeView: () -> Wrapper
    var update: (Wrapper, Context) -> Void
    var action: (() -> Void)?

    init(_ makeView: @escaping @autoclosure () -> Wrapper,
         updater update: @escaping (Wrapper) -> Void) {
        self.makeView = makeView
        self.update = { view, _ in update(view) }
    }

    func makeUIView(context: Context) -> Wrapper {
        makeView()
    }

    func updateUIView(_ view: Wrapper, context: Context) {
        update(view, context)
    }
}

struct SwiftUIView_Previews: PreviewProvider {
    static var previews: some View {
        MyView()
    }
}

如果我尝试添加这个:

[=12=].addTarget(self, action: #selector(toggleColor), for: .touchUpInside)

有:

func toggleColor() {
    isGreen = !isGreen
}

我收到这个错误:

Argument of '#selector' refers to instance method 'toggleColor()' that is not exposed to Objective-C

如果我将 @objc 添加到方法中,我会收到此错误:

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

并且由于我的 Anything 结构不是来自 SwiftUI 的 Button,我无法正常添加 action 参数

如何用这种方式将 target/action 添加到我的按钮?

这是一个可能解决方案的演示 - 我们需要在 UIKit objective-c 选择器和 SwiftUI swift 函数之间进行包装。

测试 Xcode 13.3 / iOS 15.4

这是主要部分(为简单起见,使用 UIButton 而不是 FluentUI.Button):

Anything(UIButton(type: .system)) {
    [=10=].setTitle("Try me!", for: .normal)

    [=10=].addTarget(toggleColor, action: #selector(Action.perform(sender:)), for: .touchUpInside)
    toggleColor.action = {
        isGreen.toggle()
    }
}

Complete test module is here