I am working on an image editing app for macOS in SwiftUI, but I feel like I have a lot of code duplication, where things should probably more elegant.
I have some sliders, and some bindings to make sure the values update and a processing method is called when the slider value has changed. Currently I have a binding for each slider:
let vStretch = Binding<Double>(
get: {
self.verticalStretchLevel
},
set: {
self.verticalStretchLevel = $0
applyProcessing("vertical stretch")
}
)
let straighten = Binding<Double>(
get: {
self.straightenLevel
},
set: {
self.straightenLevel = $0
applyProcessing("straighten")
}
)
let vignette = Binding<Double>(
get: {
self.vignetteLevel
},
set: {
self.vignetteLevel = $0
applyProcessing("vignette")
}
)
This is ugly right? Can anyone point me to some article, site or give me some advice on how to make this right?
Thanks in advance!
I ended up making a view for a slider, which also has the binding:
//
// SliderView.swift
//
// Created by Michel Storms on 07/12/2020.
//
import SwiftUI
struct SliderView: View {
var runFilters: () -> Void // links to function from parent view
let label: String
let level: Binding<Double>
var body: some View {
if label.count == 1 {
HStack {
Text(label).frame(width: sliderValueWidth)
Slider(value: intensity(for: level) )
TextField("", value: level, formatter: sliderFormatter(), onCommit: { self.runFilters() } ).frame(width: sliderValueWidth)
}
.onLongPressGesture{ level.wrappedValue = 0.5 ; self.runFilters() }
.onTapGesture(count: 2, perform: { level.wrappedValue = 0.5 ; self.runFilters() })
.frame(height: sliderTextSize)
.font(.system(size: sliderTextSize))
} else {
VStack {
HStack{
Text(label)
Spacer()
TextField("", value: level, formatter: sliderFormatter(), onCommit: { self.runFilters() } ).frame(width: sliderValueWidth)
}
.frame(height: sliderTextSize)
.font(.system(size: sliderTextSize))
Slider(value: intensity(for: level) ).frame(height: sliderTextSize)
}
.onLongPressGesture{ level.wrappedValue = 0.5 ; self.runFilters() }
.onTapGesture(count: 2, perform: { level.wrappedValue = 0.5 ; self.runFilters() })
.frame(height: sliderHeight)
.font(.system(size: sliderTextSize))
}
}
func intensity(for sliderLevel: Binding<Double>) -> Binding<Double> {
Binding<Double>(
get: { sliderLevel.wrappedValue },
set: { sliderLevel.wrappedValue = $0; self.runFilters() }
)
}
func sliderFormatter() -> NumberFormatter {
let formatter = NumberFormatter()
formatter.allowsFloats = true
formatter.numberStyle = .decimal
formatter.alwaysShowsDecimalSeparator = true
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 2
formatter.decimalSeparator = "."
return formatter
}
}
... and then display the sliders like this:
var body: some View {
return List {
VStack {
SliderView(runFilters: self.runFilters, label: "Exposure", level: $appState.exposureLevel)
SliderView(runFilters: self.runFilters, label: "Contrast", level: $appState.contrastLevel)
SliderView(runFilters: self.runFilters, label: "Brightness", level: $appState.brightnessLevel)
SliderView(runFilters: self.runFilters, label: "Shadows", level: $appState.shadowsLevel)
SliderView(runFilters: self.runFilters, label: "Highlights", level: $appState.highlightsLevel)
SliderView(runFilters: self.runFilters, label: "Vibrance", level: $appState.vibranceLevel)
SliderView(runFilters: self.runFilters, label: "Saturation", level: $appState.saturationLevel)
SliderView(runFilters: self.runFilters, label: "Clarity", level: $appState.clarityLevel)
SliderView(runFilters: self.runFilters, label: "Black Point", level: $appState.blackpointLevel)
if debug {
SliderView(runFilters: self.runFilters, label: "DEBUG / TEST", level: $appState.debugAndTestSliderLevel)
}
}
.font(.system(size: sliderTextSize))