将自定义 UIKit 事件从自定义 UIKit 控件公开到 SwiftUI

Posted

技术标签:

【中文标题】将自定义 UIKit 事件从自定义 UIKit 控件公开到 SwiftUI【英文标题】:Exposing custom custom UIKit events from custom UIKit controls to SwiftUI 【发布时间】:2019-10-07 10:33:45 【问题描述】:

我有一个具有自定义事件的自定义 UIKit 控件。有谁知道如何使用 Combine 框架将此事件公开给 SwiftUI?

我可以使用 Combine 的 UIViewRepresentable 呈现控件,但找不到将自定义事件或内部 UIKit 事件更改的值公开给 SwiftUI 的方法。

这是这个问题的一个具体例子:

我正在使用这个自定义 UIKit 滑块控件(因为它支持多个旋钮/值):

https://github.com/yonat/MultiSlider

它有一个更新当前值的 sliderChanged 事件。 如何使用 Combine 将此值公开给 SwiftUI?

我不能简单地传入 @ObservedObject,因为 UIViewRepresentable 不允许这样做。我也无法从事件处理程序更新 @Binding 变量,因为 UIKit 事件处理程序标有 @objc 并且它不喜欢它。

这似乎是一个非常常见的用例。我希望苹果有一个标准的解决方案。但是,我根本找不到真正适用于这些类型场景的。我在这里错过了什么?

【问题讨论】:

【参考方案1】:

我认为,这可以解决您的问题:

final class MultiSliderSwiftUI: UIViewRepresentable 
    private let valueChanged: ([CGFloat]) -> Void

    init(valueChanged: @escaping ([CGFloat]) -> Void) 
        self.valueChanged = valueChanged
    

    func makeUIView(context: UIViewRepresentableContext<MultiSliderSwiftUI>) -> MultiSlider 
        let slider = MultiSlider()
        slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)

        return slider
    

    func updateUIView(_ uiView: MultiSlider, context: UIViewRepresentableContext<MultiSliderSwiftUI>) 

    

    @objc func sliderChanged(_ slider: MultiSlider) 
        valueChanged(slider.value)
    

如果你不喜欢为 SwiftUI 视图创建 final class,你可以这样做:

struct MultiSliderSwiftUI: UIViewRepresentable 
    private let events: MultiSliderEvents

    init(valueChanged: @escaping ([CGFloat]) -> Void) 
        events = MultiSliderEvents(valueChanged: valueChanged)
    

    func makeUIView(context: UIViewRepresentableContext<MultiSliderSwiftUI>) -> MultiSlider 
        let slider = MultiSlider()
        events.addEvents(for: slider)

        return slider
    

    func updateUIView(_ uiView: MultiSlider, context: UIViewRepresentableContext<MultiSliderSwiftUI>) 

    


class MultiSliderEvents 
    private let valueChanged: ([CGFloat]) -> Void

    init(valueChanged: @escaping ([CGFloat]) -> Void) 
        self.valueChanged = valueChanged
    

    func addEvents(for slider: MultiSlider) 
        slider.addTarget(self, action: #selector(sliderChanged(_:)), for: .valueChanged)
    

    @objc func sliderChanged(_ slider: MultiSlider) 
        valueChanged(slider.value)
    

两种方式都可以使用MultiSliderSwiftUI,例如Button

MultiSliderSwiftUI  value in
    print("\(value)")

【讨论】:

感谢您的回复!非常感谢!【参考方案2】:

请尝试swiftui 分支中的MultiValueSlider,并告诉我它是否适合您。 用法示例:MultiSliderDemo。

(我是包作者)

【讨论】:

感谢您的回复!非常感谢!

以上是关于将自定义 UIKit 事件从自定义 UIKit 控件公开到 SwiftUI的主要内容,如果未能解决你的问题,请参考以下文章

Flex 4 从自定义组件调度自定义事件(为啥 flex 将自定义事件转换为 mouseevent)

七:定义全局变量的方法以及UIKIT_EXTERN简单理解

UIkit框架(14)自定义标签控制器

关于自定义 UIKit 对象的概念 q

UIKit框架(10)自定义modal过渡效果

自定义UICollectionLayout布局 —— UIKit之学习UICollectionView记录一《瀑布流》