Create custom slider in SwiftUI
In this tutorial, We’ll learn to create a custom slider in SwiftUI. We create custom sliders because the default slider of SwiftUI doesn’t give us many options for modification. If you use the default slider you can only change its tint color and background color. However, By using different components of Swiftui we can create our custom sliders.
What this tutorial will cover:
- Custom slider
- Zstack
- Rectangle
- Circle
- gesture
Let’s create our very own slider in SwiftUI.
First thing first, We are going to create the custom slider using two rectangles. The first rectangle will be equal to the complete width of the slider, and the second will use touch gestures to be interactive. Let’s how we’ll do it.
struct CustomSlider: View { @State var width1:CGFloat = 30 var totalWidth = UIScreen.main.bounds.width - 85 var body: some View { VStack{ ZStack(alignment: .leading ){ Rectangle() .fill(Color.black.opacity(0.20)) .frame(width:totalWidth, height: 6) Rectangle() .fill(Color.teal) .frame(width: self.width1,height: 6) }.padding(.top,25) }.padding() } }
Here we have emdeded a ZStatck in VStack. As Vstack align its subviews in a vertical position, ZStack aligns them in Z-Axis, Therefore, Zstack’s subviews will be stacked on top of one another.
So the first light color rectangle, which is equal to the totalWidth of the screen minus the padding area, is placed below the second dark color rectangle, which is equal to the width1.
width1 is a State variable so that it can keep track of the current selection of the slider.
Next, we need to create a circle by which users can interact and change the slider value. You can use any other shape of your own choice.
struct CustomSlider: View { @State var width1:CGFloat = 30 var totalWidth = UIScreen.main.bounds.width - 85 var body: some View { VStack{ ZStack(alignment: .leading ){ //first rectangle Rectangle() .fill(Color.black.opacity(0.20)) .frame(width:totalWidth, height: 6) //second rectangle Rectangle() .fill(Color.teal) .frame(width: self.width1,height: 6) Circle() .fill(Color.teal) .frame(width: 18,height: 18) .offset(x:self.width1) }.padding(.top,25) }.padding() } }
The offset is the circle’s position on the screen. It depends on the state variable width1. This means when the value of width1 changes it will also change the circle’s position on the screen.
The width of the second rectangle is likewise linked to the variable width1, So it will also change when the value of that variable is changed.
struct CustomSlider: View { @State var width1:CGFloat = 30 var totalWidth = UIScreen.main.bounds.width - 85 var body: some View { VStack{ ZStack(alignment: .leading ){ Rectangle() .fill(Color.black.opacity(0.20)) .frame(width:totalWidth, height: 6) Rectangle() .fill(Color.teal) .frame(width: self.width1,height: 6) Circle() .fill(Color.teal) .frame(width: 18,height: 18) .offset(x:self.width1) .gesture( DragGesture() .onChanged( { (value) in // update the width only if its greater than zero and less than the total width if value.location.x <= totalWidth-10 && value.location.x >= 0{ self.width1 = value.location.x } })) }.padding(.top,25) }.padding() } }
The if condition inside the onChanged()
method checks the Circles’ offset value. The condition makes sure that the value of the offset of the circle will never be less than zero and will never be greater than the totalWidth
.
On user interaction, the onChanged()
method will run. It will update the value of width1 when the new value is greater than zero and less than the totalWidth.
Now we’ll embed this ZStack in an HStack that will contain two textViews of the “+” and “-” signs.
struct CustomSlider: View { @State var width1:CGFloat = 30 var totalWidth = UIScreen.main.bounds.width - 85 var body: some View { VStack{ HStack(spacing: 5 ){ Text("-").font(.title).fontWeight(.bold) ZStack(alignment: .leading ){ Rectangle() .fill(Color.black.opacity(0.20)) .frame(width:totalWidth, height: 6) Rectangle() .fill(Color.teal) .frame(width: self.width1,height: 6) Circle() .fill(Color.teal) .frame(width: 18,height: 18) .offset(x:self.width1) .gesture( DragGesture() .onChanged( { (value) in if value.location.x <= totalWidth-10 && value.location.x >= 0{ self.width1 = value.location.x } })) } Text("+").font(.title).fontWeight(.bold) }.padding(.top,25) }.padding() } }
Instead of using textViews of the “+” and “-” signs, you can use imageViews as well.
And for getting the value of the slider just divide the width1, which is the current value of the slider, by totalWidth and we’ll get the value.
struct CustomSlider: View { @State var width1:CGFloat = 30 var totalWidth = UIScreen.main.bounds.width - 85 var body: some View { VStack{ Text("Value") .font(.title) .fontWeight(.bold) Text("\(self.width1/self.totalWidth)") .fontWeight(.bold) .foregroundColor(Color.teal) .padding(.top) HStack(spacing: 5 ){ Text("-").font(.title).fontWeight(.bold) ZStack(alignment: .leading ){ Rectangle() .fill(Color.black.opacity(0.20)) .frame(width:totalWidth, height: 6) Rectangle() .fill(Color.teal) .frame(width: self.width1,height: 6) Circle() .fill(Color.teal) .frame(width: 18,height: 18) .offset(x:self.width1) .gesture( DragGesture() .onChanged( { (value) in if value.location.x <= totalWidth-10 && value.location.x >= 0{ self.width1 = value.location.x } })) } Text("+").font(.title).fontWeight(.bold) }.padding(.top,25) }.padding() } }
Also read: How to display or load an image from URL in SwiftUI
Leave a Reply