Due to the limitations of SwiftUI, I usually need to jump back into to UIKit and take advantage of UIKit components. 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, e.g. first name, last name, email, and password. I usually need to create separate UIViewRepresentable UIKit components for each.
I'm currently working on a project where I have 2 simple sliders that update 2 different labels when they are moved. 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.
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. 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.
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:
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, e.g.
@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.
@Statevar for each slider - Mac3n