0

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!

Michel Storms
  • 336
  • 3
  • 10
  • 1
    You can use something like [this](https://stackoverflow.com/a/63793229/5941807) to set and get values and plus save like UserDefaults – Joannes Nov 17 '20 at 09:06

1 Answers1

0

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))
Michel Storms
  • 336
  • 3
  • 10