UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Day 67: Disabling slider problem.

Forums > 100 Days of SwiftUI

Hello. I have just finished day 67 and solved challenges for instaFilter. I have added 4 slider for different filters to my view. I want sliders to be active if currentFilter have parameter for this slider parameter. For example if I choose Crystallize, only "radius" slider should be active. Although I can not change Intensity parameter and Text color become gray as expected when I switched to crystallize, slider color still looks active (but I can not change the value as should be). Do you have any idea, why the slider looks active although it is inactive?

import PhotosUI
import CoreImage
import CoreImage.CIFilterBuiltins
import SwiftUI

struct ContentView: View {
    @State private var image: Image?
    @State private var filterIntensity = 0.5
    @State private var filterRadius = 0.5
    @State private var filterScale = 0.5
    @State private var filterSaturationAmount = 0.0
    @State private var showingImagePicker = false
    @State private var inputImage: UIImage?
    @State private var processedImage: UIImage?
    @State private var currentFilter: CIFilter = CIFilter.sepiaTone()
    let context = CIContext()

    @State private var showingFilterSheet = false

    var body: some View {
        NavigationView {
            VStack {
                ZStack {
                    Rectangle()
                        .fill(.secondary)

                    Text("Tap to select a picture")
                        .foregroundColor(.white)
                        .font(.headline)

                    image?
                        .resizable()
                        .scaledToFit()
                }
                .onTapGesture {
                    showingImagePicker = true
                }
                HStack {
                    VStack {
                        Text("Intensity")
                            .foregroundColor(!currentFilter.inputKeys.contains(kCIInputIntensityKey) ? .gray: .black)
                        Slider(value: $filterIntensity)
                            .disabled(!currentFilter.inputKeys.contains(kCIInputIntensityKey))
                            .onChange(of: filterIntensity) { _ in
                                applyProcessing()
                            }
                        Text("Radius")
                            .foregroundColor(!currentFilter.inputKeys.contains(kCIInputRadiusKey) ? .gray: .black)
                        Slider(value: $filterRadius)
                            .disabled(!currentFilter.inputKeys.contains(kCIInputRadiusKey))
                            .onChange(of: filterRadius) { _ in
                                applyProcessing()
                            }
                        Text("Scale")
                            .foregroundColor(!currentFilter.inputKeys.contains(kCIInputScaleKey) ? .gray: .black)
                        Slider(value: $filterScale)
                            .disabled(!currentFilter.inputKeys.contains(kCIInputScaleKey))
                            .onChange(of: filterScale) { _ in
                                applyProcessing()
                            }
                        Text("Saturation Amount")
                            .foregroundColor(!currentFilter.inputKeys.contains(kCIInputAmountKey) ? .gray: .black)
                        Slider(value: $filterSaturationAmount, in: -1.0...1.0)
                            .disabled(!currentFilter.inputKeys.contains(kCIInputAmountKey))
                            .onChange(of: filterSaturationAmount) { _ in
                                applyProcessing()
                            }

                    }
                }
                .padding(.vertical)

                HStack {
                    Button("Change Filter") {
                        showingFilterSheet = true
                    }

                    Spacer()
                        Button("Save", action: save)
                        .disabled((inputImage == nil))
                    // save the picture
                }
            }
        }
        .padding([.horizontal, .bottom])
        .navigationTitle("Instafilter")
        .sheet(isPresented: $showingImagePicker) {
            ImagePicker(image: $inputImage)

        }
        .confirmationDialog("Select a filter", isPresented: $showingFilterSheet) {
            VStack{
                Group {
                    Button("Comic Effect") {setFilter(CIFilter.comicEffect()) }
                    //comic use no parameter
                    Button("Crystallize") { setFilter(CIFilter.crystallize()) }
                    Button("Edges") { setFilter(CIFilter.edges()) }
                    Button("Dither") {setFilter(CIFilter.dither()) }
                    //dither use intensity
                    Button("Gaussian Blur") { setFilter(CIFilter.gaussianBlur()) }
                }
                Group {
                    Button("Pixellate") { setFilter(CIFilter.pixellate()) }
                    Button("Sepia Tone") { setFilter(CIFilter.sepiaTone()) }
                    Button("Unsharp Mask") { setFilter(CIFilter.unsharpMask()) }
                    Button("Vibrance") {setFilter(CIFilter.vibrance()) }
                    //vibrance use amount parameter
                    Button("Vignette") { setFilter(CIFilter.vignette()) }
                }
                    Button("Cancel", role: .cancel) { }

            }
        }
        .onChange(of: inputImage) { _ in loadImage()}
    }
    func loadImage() {
        guard let inputImage = inputImage else { return }

        let beginImage = CIImage(image: inputImage)
        currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
        applyProcessing()
    }

    func save() {
        guard let processedImage = processedImage else { return }

        let imageSaver = ImageSaver()

        imageSaver.successHandler = {
            print("Success")
        }

        imageSaver.errorHandler = {
            print("Oops: \($0.localizedDescription)")
        }

        imageSaver.writeToPhotoAlbum(image: processedImage)
    }

    func applyProcessing() {
        let inputKeys = currentFilter.inputKeys

        if inputKeys.contains(kCIInputIntensityKey) { currentFilter.setValue(filterIntensity, forKey: kCIInputIntensityKey) }
        if inputKeys.contains(kCIInputRadiusKey) { currentFilter.setValue(filterRadius * 200, forKey: kCIInputRadiusKey) }
        if inputKeys.contains(kCIInputScaleKey) { currentFilter.setValue(filterScale * 10, forKey: kCIInputScaleKey) }
        if inputKeys.contains(kCIInputAmountKey) { currentFilter.setValue(filterSaturationAmount , forKey: kCIInputAmountKey) }

        guard let outputImage = currentFilter.outputImage else { return }

        if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
            let uiImage = UIImage(cgImage: cgimg)
            image = Image(uiImage: uiImage)
            processedImage = uiImage
        }
    }

    func setFilter(_ filter: CIFilter) {
        currentFilter = filter
        loadImage()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2      

Posted on Paul's birthday in 2022. Happy birthday, sensei!


Dre has a slider question:

Why does the slider look active although it is disabled?

You posted a lot of code! Much of your code has nothing to do with your problem. I think this is part of what clouds new developers' minds. Their quest is to get their application working. But all the excess code fogs the essence of the problem.

Loading images? kCIInputRadiusKey? ImageSaver? None of this is essential to your issue. Focus!

Instead of solving your solution's goals, take a step back to the playground, and just pick ONE piece of equipment and have fun!

Every playground has a slide! Let's have fun with that for a while. Focus on the slide (Slider!).

struct SliderTestView: View {
    @State private var filterIntensity = 20.0  // set the initial slider value
    @State private var filterIsActive  = true
    // encapsulate your intentions as computed variables where possible
    var filterColor: Color { filterIsActive ? .accentColor : .gray.opacity(0.3) }
    var intensityDisplayString: String { String(format: "%.1f", filterIntensity) }

    var body: some View {
        VStack(spacing: 20) {
            Toggle("Intensity", isOn: $filterIsActive) // Turn the filter ON or OFF
                .frame(width: 150)
            Divider()
            // These react when filterIsActive variable changes
            Group {
                Image(systemName: "globe")
                Text( "Intensity: " + intensityDisplayString)  // simplify!
                Slider(value: $filterIntensity, in: 0...50)
                // slider color does not use foregroundColor, instead use accentColor
                    .accentColor(filterColor)   // Slider color
                    .disabled(!filterIsActive)  // disable slider
            }
            .foregroundColor( filterColor )     // both image and text
        }
    }
}

// Boilerplate
struct SliderTestView_Previews: PreviewProvider {
    static var previews: some View { SliderTestView() }
}

Please return here and share with us what you learned. Also would like to know how you implemented your improved solution.

Nick at Swiftful Thinking has some additional thoughts on Sliders(). See -> Swiftful Thinking: Sliders

2      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.