This view is a UIKit slider adapted to my SwiftUI project because in SwiftUI Slider
cannot change its track color, which is probably a bug since you should be able to change it with .accentColor
. Anyway, this slider changes its track color according to its value, from green to red. The whole thing works perfectly (although my gradients aren't that good yet) if value
is bound to a normal @State
property, but the second you try to attach it to a property of an @ObservedObject
it breaks and, although the track color still works, it never changes the underlying value. I would like to think that this is just a bug right now but it's more likely there's something here that needs to be fixed.
struct RedscaleSlider: UIViewRepresentable {
@Binding var value: Double
var min: Double
var max: Double
class Coordinator: NSObject {
@Binding var value: Double
var min: Double
var max: Double
init(value: Binding<Double>, min: Double = 0, max: Double = 100) {
_value = value
self.min = min
self.max = max
}
@objc func valueChanged(_ sender: UISlider) {
self.value = Double(sender.value)
sender.minimumTrackTintColor = green_to_red_gradient(value: (Double(sender.value) - min) / (max - min)).into_UIKit_color()
}
}
var thumb_color: UIColor = .white
var track_color: UIColor = .systemBlue
func makeUIView(context: Context) -> UISlider {
let slider = UISlider(frame: .zero)
slider.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged),
for: .valueChanged
)
slider.thumbTintColor = thumb_color
slider.minimumTrackTintColor = track_color
slider.minimumValue = Float(min)
slider.maximumValue = Float(max)
slider.value = Float(value)
return slider
}
func updateUIView(_ UI_View: UISlider, context: Context) {
UI_View.value = Float(self.value)
}
func makeCoordinator() -> RedscaleSlider.Coordinator {
Coordinator(value: $value, min: self.min, max: self.max)
}
}
EDIT: Example of how it should be able to be used:
class ViewModel: ObservableObject {
@Published var danger_level: Double
}
struct ExampleView: View {
@ObservedObject var view_model = ViewModel(danger_level: 50)
var body: some View {
VStack {
Text(view_model.danger_level.description)
RedscaleSlider(value: $view_model.danger_level)
// should update view model just like a Stepper would
}
}
}
@Binding
is only supposed to be used in a View
/ UIViewRepresentable
Instead of having an @Binding
in the Coordinator
switch the init
to receive the init(_ parent: RedscaleSlider)
then use parent.value = Double(sender.value)
import SwiftUI
class RedscaleSliderViewModel : ObservableObject {
@Published var value : Double = 5
@Published var danger_level: Double = 7.5
}
struct ParentRedscaleSlider: View{
//@State var value: Double = 5
@ObservedObject var vm = RedscaleSliderViewModel()
var body: some View {
VStack{
Text(vm.danger_level.description)
RedscaleSlider(value: $vm.danger_level, min: 0, max: 10)
}
}
}
struct RedscaleSlider: UIViewRepresentable {
//@EnvironmentObject var vm: RedscaleSliderViewModel
@Binding var value: Double
var min: Double
var max: Double
class Coordinator: NSObject {
var parent: RedscaleSlider
init(_ parent: RedscaleSlider) {
self.parent = parent
}
@objc func valueChanged(_ sender: UISlider) {
let senderVal = Double(sender.value)
self.parent.value = senderVal
//Missing code
//sender.minimumTrackTintColor = green_to_red_gradient(value: (Double(sender.value) - min) / (max - min)).into_UIKit_color()
}
}
var thumb_color: UIColor = .white
var track_color: UIColor = .systemBlue
func makeUIView(context: Context) -> UISlider {
let slider = UISlider(frame: .zero)
slider.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged),
for: .valueChanged
)
slider.thumbTintColor = thumb_color
slider.minimumTrackTintColor = track_color
slider.minimumValue = Float(min)
slider.maximumValue = Float(max)
slider.value = Float(value)
return slider
}
func updateUIView(_ UI_View: UISlider, context: Context) {
UI_View.value = Float(self.value)
}
func makeCoordinator() -> RedscaleSlider.Coordinator {
Coordinator(self)
}
}
struct RedScaleSlider_Previews: PreviewProvider {
static var previews: some View {
ParentRedscaleSlider()
}
}
accentColor
is working fine for me like that..
Also your code works fine with a ObservableObject. Here is the demo code:
class ViewModel : ObservableObject {
@Published var double : Double = 0.0
}
struct ContentView : View {
@State private var value: Double = 0
@ObservedObject var viewModel = ViewModel()
var body : some View {
Slider(value: $value, in: -100...100, step: 0.1)
.accentColor(.red) //<< here accent color
RedscaleSlider(value: $viewModel.double, min: 5.0, max: 250.0)
Text(String(viewModel.double))
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.