SwiftUI Circular Slider

Kazi Munshimun Nabi
5 min readSep 11, 2020

--

Today we would like to create a Circular Slider in SwiftUI like following dribble design:

https://dribbble.com/shots/4153860-Home-smart-ios-app

Final Result:

Final Result

I am using Xcode 12 beta 6. Lets start by opening the Xcode.

Create New Project -> iOS -> App

Give a app name and make sure to select SwiftUI in Interface drop down.

Creating new projet

First let remove the Text View from body and add a ZStack and add a Rectangle() inside to fill up the whole screen, also add .edgesIgnoringSafeArea(.all) to the ignore the safe area. Now we have a black background, next we want to fill the rectangle with a dark purple color like the design.

Creating background

Next we will create a View called TemperatureControlView for temperature control, also add a ZStack in the view. Lastly we add TemperatureControlView() in our ContentView after our Rectangle

adding TemperatureControlView

Next we need some state variable for the temperature value and angle rotation of the Knob circle(circle we will drag to change the value). Also we need some static values for the control for :

  1. Total Value of the control circle
  2. Maximum value of the control (difference between total value and maximum value will be clear when we create a control where we do not want Knob circle to go 360 degree)
  3. Minimum value of the control
  4. Radius of the control
  5. Knob circle radius

To keep all the variable in one place lets create a struct called Config and add the following variables :

and add the config variable in TemperatureControlView view with the @State variable for temperature and angle value. Code so far in TemperatureControlView :

Now lets start building the control. First we add a Circle with frame of 2*radius in the ZStack, this Circle will show current temperature progress in the View. We will add trim modifier to show the progress and add stroke modifier with blue color and lineWidth of 4.

Circle()
.trim(from: 0.0, to: 0.5) // we will change the value of to(0.5) later
.stroke(Color.blue, lineWidth: 4)
.frame(width: config.radius * 2, height: config.radius * 2)

we need to rotate -90 degree to start from the top center

Code until now in TemperatureControlView:

Circle trim from 0.0 to 0.5

Now lets change the to value in trim modifier. As trim value range from 0.0 to 1.0, if we want to show our desired temperature value we need to divide our temperature value with total value. So we will change the value 0.5 to temperatureValue/config.totalValue. If we run the app now we will not see any progress because now our temperatureValue is 0.0.

Next lets add the Knob circle that we will drag to change the temperature value.

Circle()
.fill(Color.blue)
.frame(width: config.knobRadius * 2, height: config.knobRadius * 2)
.padding(10)
.offset(y: -config.radius)

Our initial knob circle is 30 * 30, so we add padding so that it is easier for user to grab in the screen. Also offset -radius of control so it sits top middle initially. Next we need to rotate the Knob and add dragGesture modifier.

Circle()
.fill(Color.blue)
.frame(width: config.knobRadius * 2, height: config.knobRadius * 2)
.padding(10)
.offset(y: -config.radius)
.rotationEffect(Angle.degrees(Double(angleValue)))
.gesture(DragGesture(minimumDistance: 0.0)
.onChanged({ value in

}))

We need to do something in the onChanged to rotate the Knob as well as change temperature value, lets add a function to do that

there is little bit of math involved

Getting angle from touch location

Basically what we are doing is taking dragLocation in CGPoint and converting it to how much angle we need to rotate the Knob and and converting the angle to temperature value. Now we need to call this change function from onChanged in gesture. And we have our Circular Slider working.

Now we want see the temperature value changing so lets add a Text after the knob circle

Text(“\(String.init(format: “%.0f”, temperatureValue)) º”)
.font(.system(size: 60))
.foregroundColor(.white)

if we run the project now we will see the temperature change when we drag the Knob circle.

Now lets add the background to match the design. If we look at the design there are two circle, one is black slightly bigger than the control circle and on dotted circle in grey. If we add following code at the beginning of ZStack we can achieve that:

Circle()
.frame(width: config.radius * 2, height: config.radius * 2)
.scaleEffect(1.2)

Circle()
.stroke(Color.gray,
style: StrokeStyle(lineWidth: 3, lineCap: .butt, dash: [3, 23.18]))
.frame(width: config.radius * 2, height: config.radius * 2)

Math behind dash value array [3, 23.18] is we want each stroke of width and height 3. Total perimeter of circle is 2 * pi * r = 2 * 3.1416 * 125 = 785.4, if we want 30 dot/small circle then that is 3 * 30 = 90. So remaining space is (785.4–90) = 695.4, if we divide that by 30 we have 23.18.

Last thing we want to do is if we see the design in first image when temperature is low then progress arc and knob circle color is blue but in second image when temperature is high then color change to red. I am guessing if maximum temperature is 40 when temperature value pass 20 then color change to red. We add following condition to Knob circle fill and progress circle stroke :

temperatureValue < config.maximumValue/2 ? Color.blue : Color.red

Final Code:

Final Code

Final Result:

Final Result

Hope that was helpful. In next story we will try to create circular slider where knob circle does not go all 360 degree.

--

--

Kazi Munshimun Nabi

Enjoy working on iOS. Learning SwiftUI by replicating unique and complex dribble UI. Youtube Channel: https://www.youtube.com/c/AnikMunshimunNabi/videos