Как повторно использовать компонент UIViewRepresentable UIKit? - PullRequest
2 голосов
/ 10 марта 2020

Из-за ограничений SwiftUI мне обычно приходится возвращаться к UIKit и использовать компоненты UIKit. Это все хорошо, но я обнаружил, что мне обычно нужно продолжать создавать отдельные структуры, чтобы содержать нужные мне функции.

Например. Допустим, у меня есть форма с 4 полями, например, имя, фамилия, адрес электронной почты и пароль. Мне обычно нужно создавать отдельные компоненты UIViewRepresentable UIKit для каждого.

В настоящее время я работаю над проектом, в котором у меня есть 2 простых ползунка, которые обновляют 2 разных метки при их перемещении. Повторное использование одного и того же компонента UIViewRepresentable UISlider в SwiftUI рассматривает оба ползунка как 1, поскольку они оба ссылаются на одно и то же свойство модели представления, поэтому мне нужно создавать отдельные компоненты, а не использовать их повторно.

Я подумал, что я мог бы также просто увидеть если есть более эффективный способ сделать это, поскольку я уже давно копирую код, и обычно в коде нет большой разницы, кроме таких вещей, как тип отображаемой клавиатуры, colors et c .

Ниже приведен пример того, как я подключаюсь к UIKit из SwiftUI. Я создаю экран, который позволит выбирать коэффициенты ставок и ставки с помощью ползунков.

Я отслеживаю изменение значения ползунка и сохраняю значение в переменной @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 моей модели представления, чтобы она вместо этого получала доступ к переменной stakes, например,

    @objc func valueChanged(_ sender: UISlider) {

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

Вот так я работаю месяцами, но это всегда было неправильно. Тем не менее, я не могу придумать другой способ приблизиться к этому.

Есть предложения? Как вы обычно справляетесь с такой ситуацией?

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 10 марта 2020

новый ответ, теперь с 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.

См. этот пример.

Все, что вам нужно сделать, это расширить вашу viewModel с помощью ваших переменных-слайдера (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
            }))
        }
    }
}
0 голосов
/ 10 марта 2020

хорошо, я понял, как вы можете это сделать без UIViewRepresentable.

См. Этот пример.

Все, что вам нужно сделать, это расширить вашу viewModel с помощью ваших переменных-слайдера (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
            }))
        }
    }
}
...