如何重用UIViewRepresentable UIKit组件?

时间:2020-03-10 04:19:13

标签: swift cocoa-touch uikit swiftui swiftui-form

由于SwiftUI的局限性,我通常需要跳回到UIKit并利用UIKit组件。一切都很好,但是我发现我通常需要继续创建单独的结构来容纳所需的功能。

例如。假设我有一个包含4个字段的表单,例如名,姓,电子邮件和密码。我通常需要为每个组件创建单独的UIViewRepresentable UIKit组件。

我目前正在一个项目中,我有2个简单的滑块,它们在移动时会更新2个不同的标签。在SwiftUI中重用相同的UIViewRepresentable UISlider组件会将两个滑块都视为1,因为它们都引用相同的视图模型属性,因此我需要创建单独的组件而不是重用它们。

我想我最好还是看看是否有一种更有效的方法来执行此操作,因为我已经复制了一段时间了,并且除了诸如此类的东西外,代码通常没有太大区别显示的键盘类型,颜色等。

下面是我从SwiftUI进入UIKit的示例。我正在创建一个屏幕,该屏幕允许使用滑块选择下注赔率和赌注。

我跟踪何时更改滑块的值,并将该值存储在视图模型的@Published变量中。

self.bViewModel.odds

我通过使用滑块的视图中的环境变量访问此视图模型,并使用它来更新文本标签。

VStack {
    HStack(spacing: 0) {
        Text("\(self.bViewModel.odds)")
             .font(Font.system(size: 15, weight: .semibold, design: .default))
             .foregroundColor(Color("CustomPurple"))
            .frame(minHeight: 0)
            .padding(.top, 3)
            .padding(.bottom, 3)

         Spacer()
    }

    SliderView()
        .frame(minHeight: 25, maxHeight: 25)

}
.padding(.bottom, 30)

SliderView结构如下:

struct SliderView: UIViewRepresentable {

    final class Coordinator: NSObject {

        @EnvironmentObject var bViewModel: BViewModel

        init(bViewModel: EnvironmentObject<BViewModel>)
        {
            self._bViewModel = bViewModel
        }

        // Create a valueChanged(_:) action
        @objc func valueChanged(_ sender: UISlider) {

            self.bViewModel.odds = Double(sender.value)
        }

    }

    func makeCoordinator() -> SliderView.Coordinator {
        Coordinator(bViewModel: self._bViewModel)
    }

    @EnvironmentObject var bViewModel: BViewModel

    func makeUIView(context: Context) -> UISlider {
        let slider = UISlider(frame: .zero)
        slider.minimumValue = 0.00
        slider.maximumValue = 100.00

        slider.addTarget(
             context.coordinator,
             action: #selector(Coordinator.valueChanged(_:)),
             for: .valueChanged
           )

        return slider
    }

    func updateUIView(_ uiView: UISlider, context: Context) {

    }
}

现在,我需要一个用于选择注额的滑块,但是如果不编写大量代码使其无法将其当作其他滑块,就无法从上方重用同一滑块。复制代码并更改我访问视图模型的odds变量的行会更容易,例如,它访问桩类变量,例如

    @objc func valueChanged(_ sender: UISlider) {

        self.bViewModel.stake = Double(sender.value)
    }

这是我工作了几个月的方法,但是一直感觉不对。但是,我似乎无法想到另一种方法来解决此问题。

有什么建议吗?您通常如何处理这种情况?

谢谢。

2 个答案:

答案 0 :(得分:2)

新答案,现在带有EnvironmentObject

import SwiftUI
import Combine

class ViewModel : ObservableObject {

    @Published var sliderValue1 : Double = 3.14
    @Published var sliderValue2 : Double = 42.0
    @Published var sliderValue3 : Double = 3.14
    @Published var sliderValue4 : Double = 42.0
}

struct SliderView: UIViewRepresentable {

    @Binding var value : Double

    class Coordinator: NSObject {
        var sliderView: SliderView

        init(_ sliderView: SliderView) {
            self.sliderView = sliderView
        }

        @objc func valueChanged(_ slider: UISlider) {
            sliderView.value = Double(slider.value)
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UISlider {
        let slider = UISlider(frame: .zero)
        slider.minimumValue = 0.00
        slider.maximumValue = 100.00

        slider.addTarget(
             context.coordinator,
             action: #selector(Coordinator.valueChanged(_:)),
             for: .valueChanged
           )

        return slider
    }

    func updateUIView(_ uiView: UISlider, context: Context) {

    }
}

struct ContentView : View {

    @EnvironmentObject var viewModel : ViewModel

    var body: some View {
        VStack {
            Text("\(self.viewModel.sliderValue1)")
            Slider(value: Binding<Double>(get:
                { return self.viewModel.sliderValue1 },
                                          set: {
                                            print($0)
                                            self.viewModel.sliderValue1 = $0
            }))

            Text("\(self.viewModel.sliderValue2)")
            Slider(value: Binding<Double>(get:
                { return self.viewModel.sliderValue2 },
                                          set: {
                                            print($0)
                                            self.viewModel.sliderValue2 = $0
            }))


            Text("\(viewModel.sliderValue3)")
            SliderView(value: Binding<Double>(get:
                { return self.viewModel.sliderValue3 },
                                          set: {
                                            print($0)
                                            self.viewModel.sliderValue3 = $0
            }))
            Text("\(viewModel.sliderValue4)")
            SliderView(value: Binding<Double>(get:
                { return self.viewModel.sliderValue4 },
                                          set: {
                                            print($0)
                                            self.viewModel.sliderValue4 = $0
            }))
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {

        var value1 : Double = 0.4
        var value2 : Double = 0.4
        var value3 : Double = 0.4
        var value4 : Double = 0.4

        return ContentView().environmentObject(ViewModel())
    }
}

好吧,因为您很有礼貌并且很快就回答了,所以我尝试了一下,希望现在我得到了您需要的东西;)

我确实将“旧滑块”放进去了,但是下面的两个滑块来自我的UIViewRepresentable。

新答案:

struct SliderView: UIViewRepresentable {

    @Binding var value : Double

    class Coordinator: NSObject {
        var sliderView: SliderView

        init(_ sliderView: SliderView) {
            self.sliderView = sliderView
        }

        @objc func valueChanged(_ slider: UISlider) {
            sliderView.value = Double(slider.value)
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UISlider {
        let slider = UISlider(frame: .zero)
        slider.minimumValue = 0.00
        slider.maximumValue = 100.00

        slider.addTarget(
             context.coordinator,
             action: #selector(Coordinator.valueChanged(_:)),
             for: .valueChanged
           )

        return slider
    }

    func updateUIView(_ uiView: UISlider, context: Context) {

    }
}

struct ContentView : View {

    @State var sliderValue1 : Double
    @State var sliderValue2 : Double

    @State var sliderValue3 : Double
    @State var sliderValue4 : Double

    var body: some View {
        VStack {
            Text("\(sliderValue1)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue1 },
                                          set: {
                                            print($0)
                                            self.sliderValue1 = $0
            }))

            Text("\(sliderValue2)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue2 },
                                          set: {
                                            print($0)
                                            self.sliderValue2 = $0
            }))


            Text("\(sliderValue3)")
            SliderView(value: Binding<Double>(get:
                { return self.sliderValue3 },
                                          set: {
                                            print($0)
                                            self.sliderValue3 = $0
            }))
            Text("\(sliderValue4)")
            SliderView(value: Binding<Double>(get:
                { return self.sliderValue4 },
                                          set: {
                                            print($0)
                                            self.sliderValue4 = $0
            }))
        }
    }
}

旧答案:

好的,我弄清楚了没有UIViewRepresentable怎么办。

请参阅此示例。

所有您需要做的就是使用您的sliderVariables(sliderValue1或您喜欢的任何名称)和

扩展viewModel。
struct ContentView : View {

    @State var sliderValue1 : Double
    @State var sliderValue2 : Double

    var body: some View {
        VStack {
            Text("\(sliderValue1)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue1 },
                                          set: {
                                            print($0)
                                            self.sliderValue1 = $0
            }))

            Text("\(sliderValue2)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue2 },
                                          set: {
                                            print($0)
                                            self.sliderValue2 = $0
            }))
        }
    }
}

答案 1 :(得分:0)

好的,我弄清楚了没有UIViewRepresentable怎么办。

请参阅此示例。

所有您需要做的就是用您的sliderVariables(sliderValue1或您喜欢的任何名称)扩展viewModel,并使用这些值代替我的State-variables。

struct ContentView : View {

    @State var sliderValue1 : Double
    @State var sliderValue2 : Double

    var body: some View {
        VStack {
            Text("\(sliderValue1)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue1 },
                                          set: {
                                            print($0)
                                            self.sliderValue1 = $0
            }))

            Text("\(sliderValue2)")
            Slider(value: Binding<Double>(get:
                { return self.sliderValue2 },
                                          set: {
                                            print($0)
                                            self.sliderValue2 = $0
            }))
        }
    }
}