简体   繁体   English

如何重用 UIViewRepresentable UIKit 组件?

[英]How do I reuse a UIViewRepresentable UIKit component?

Due to the limitations of SwiftUI, I usually need to jump back into to UIKit and take advantage of UIKit components.由于 SwiftUI 的限制,我通常需要跳回 UIKit 并利用 UIKit 组件。 This is all good, but I'm finding that I usually need to keep creating separate structs to hold the functionality I need.这一切都很好,但我发现我通常需要不断创建单独的结构来保存我需要的功能。

For example.例如。 Let's say I have a form with 4 fields, eg first name, last name, email, and password.假设我有一个包含 4 个字段的表单,例如名字、姓氏、电子邮件和密码。 I usually need to create separate UIViewRepresentable UIKit components for each.我通常需要为每个组件创建单独的 UIViewRepresentable UIKit 组件。

I'm currently working on a project where I have 2 simple sliders that update 2 different labels when they are moved.我目前正在开发一个项目,其中有 2 个简单的滑块,可在移动时更新 2 个不同的标签。 Reusing the same UIViewRepresentable UISlider component in SwiftUI treats both sliders as 1, because they both reference the same view model property, hence me needing to create separate components rather than reusing them.在 SwiftUI 中重用相同的 UIViewRepresentable UISlider 组件会将两个滑块视为 1,因为它们都引用相同的视图模型属性,因此我需要创建单独的组件而不是重用它们。

I figured I might as well just see if there is a more efficient way to do this as I've been duplicating code for quite some time now, and there usually isn't much difference in the code apart from things like what kind of keypad to show, colours etc.我想我不妨看看是否有更有效的方法来做到这一点,因为我已经复制代码很长一段时间了,除了什么样的键盘之类的东西之外,代码中通常没有太大区别显示,颜色等

Below, is an example of how I tap into UIKit from SwiftUI.下面是我如何从 SwiftUI 使用 UIKit 的示例。 I'm creating a screen that will allow bet odds and a stake to be selected using sliders.我正在创建一个屏幕,允许使用滑块选择投注赔率和赌注。

I track when the slider's value is changed and store the value in a @Published var from my view model.我跟踪滑块的值何时更改并将值存储在我的视图模型中的 @Published var 中。

self.bViewModel.odds

I access this view model through an environment variable in the view where the slider is used, and use it to update a Text label.我通过使用滑块的视图中的环境变量访问此视图模型,并使用它来更新文本标签。

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)

The SliderView struct looks like this: 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) {

    }
}

Now, I need a slider for the stake selection, but I can't reuse the same slider from above without writing a bunch of code to make it treat it as a different slider.现在,我需要一个用于选择赌注的滑块,但是如果不编写一堆代码使其将其视为不同的滑块,我就无法重用上面的相同滑块。 It's just easier to duplicate the code and change the line where I access my view model's odds variable so that it access the stakes variable instead, eg复制代码并更改我访问我的视图模型的赔率变量的行更容易,以便它访问赌注变量,例如

    @objc func valueChanged(_ sender: UISlider) {

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

This is how I've been working for months, but it has always felt wrong.这就是我几个月来一直工作的方式,但总感觉不对。 However, I can't seem to think of another way to approach this.但是,我似乎想不出另一种方法来解决这个问题。

Any suggestions?有什么建议? How do you usually handle this kind of situation?你通常如何处理这种情况?

Thanks in advance.提前致谢。

new Answer, now with EnvironmentObject新答案,现在使用 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())
    }
}

ok, because you are so polite and really answer quickly i tried it and hopefully now i got, what you needed ;)好的,因为你很有礼貌而且真的很快回答我试过了,希望现在我得到了你需要的东西;)

i did let the "old sliders" in it, but now the lower two sliders are from my UIViewRepresentable.我确实让“旧滑块”在里面,但现在下面的两个滑块来自我的 UIViewRepresentable。

NEW ANSWER:新答案:

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
            }))
        }
    }
}

OLD ANSWER:旧答案:

ok, i figured it out how you can do it without UIViewRepresentable.好的,我想出了如何在没有 UIViewRepresentable 的情况下做到这一点。

See this example.请参阅此示例。

All you have to do is extend your viewModel with your sliderVariables (sliderValue1 or whatever name you like) and您所要做的就是使用您的滑块变量(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
            }))
        }
    }
}

ok, i figured it out how you can do it without UIViewRepresentable.好的,我想出了如何在没有 UIViewRepresentable 的情况下做到这一点。

See this example.请参阅此示例。

All you have to do is extend your viewModel with your sliderVariables (sliderValue1 or whatever name you like) and use these values instead of my State-variables.您所要做的就是使用您的滑块变量(sliderValue1 或您喜欢的任何名称)扩展您的视图模型,并使用这些值而不是我的状态变量。

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
            }))
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM