Home > Software design >  How to implement a slider whose minimum track color is begin from center(value=0) not left in SwiftU
How to implement a slider whose minimum track color is begin from center(value=0) not left in SwiftU

Time:01-17

I want to custom Slider in SwiftUI. Just something like this.

enter image description here

I tried Slider with GeometryReader but it isn't working.

//MARK: Left - Right Balance
GeometryReader { geo in
    VStack {
        Text("\(String(format: "%.2f", balanceVolume))")
        HStack {
            Text("L")
            Slider(value: $balanceVolume, in: minValue...maxValue, step: 0.1) {editing in
                print("editing", editing)
                isEditing = editing
                if !editing {
                    player.pan = Float(balanceVolume)
                }
             }
             .tint(.none)
             .accentColor(.gray)
             Text("R")
        }
     }
     .padding(20)
}

Thank you you all.

CodePudding user response:

I have created a simple custom slider, I hope it helps

Output: enter image description here

Use:

struct slider: View {
    @State var sliderPosition: Float = 50
    var body: some View {
        SliderView(value: $sliderPosition, bounds: 1...100).padding(.all)
        
    }
}

Code:

struct SliderView: View {
    let currentValue: Binding<Float>
    let sliderBounds: ClosedRange<Int>
    
    public init(value: Binding<Float>, bounds: ClosedRange<Int>) {
        self.currentValue = value
        self.sliderBounds = bounds
    }
    
    var body: some View {
        GeometryReader { geomentry in
            sliderView(sliderSize: geomentry.size)
        }
    }
    
    
    @ViewBuilder private func sliderView(sliderSize: CGSize) -> some View {
        let sliderViewYCenter = sliderSize.height / 2
        let sliderViewXCenter = sliderSize.width / 2
        ZStack {
            RoundedRectangle(cornerRadius: 2)
                .fill(Color.gray)
                .frame(height: 3)
            ZStack {
                let sliderBoundDifference = sliderBounds.count
                let stepWidthInPixel = CGFloat(sliderSize.width) / CGFloat(sliderBoundDifference)
                
                
                let thumbLocation = CGFloat(currentValue.wrappedValue) * stepWidthInPixel
                
                // Path between starting point to thumb
                lineBetweenThumbs(from: .init(x: sliderViewXCenter, y: sliderViewYCenter), to: .init(x: thumbLocation, y: sliderViewYCenter))
                
                // Thumb Handle
                let thumbPoint = CGPoint(x: thumbLocation, y: sliderViewYCenter)
                thumbView(position: thumbPoint, value: Float(currentValue.wrappedValue))
                    .highPriorityGesture(DragGesture().onChanged { dragValue in
                        
                        let dragLocation = dragValue.location
                        let xThumbOffset = min(dragLocation.x, sliderSize.width)
                        
                        let newValue = Float(sliderBounds.lowerBound / sliderBounds.upperBound)   Float(xThumbOffset / stepWidthInPixel)
                        if newValue > Float(sliderBounds.lowerBound) && newValue < Float(sliderBounds.upperBound   1) {
                            currentValue.wrappedValue = newValue
                            
                        }
                    })
                
                
            }
        }
    }
    
    @ViewBuilder func lineBetweenThumbs(from: CGPoint, to: CGPoint) -> some View {
        Path { path in
            path.move(to: from)
            path.addLine(to: to)
        }.stroke(Color.blue, lineWidth: 4)
    }
    
    @ViewBuilder func thumbView(position: CGPoint, value: Float) -> some View {
        ZStack {
            Text(String(round(value)))
                .font(.headline)
                .offset(y: -20)
            Circle()
                .frame(width: 24, height: 24)
                .foregroundColor(.accentColor)
                .shadow(color: Color.black.opacity(0.16), radius: 8, x: 0, y: 2)
                .contentShape(Rectangle())
        }
        .position(x: position.x, y: position.y)
    }
}
  • Related