SoatDev IT Consulting
SoatDev IT Consulting
  • About us
  • Expertise
  • Services
  • How it works
  • Contact Us
  • News
  • June 23, 2023
  • Rss Fetcher
Photo by Andreas Haslinger on Unsplash

In this tutorial, we will create a joystick-like control using SwiftUI. The control consists of a larger circle that can be dragged around the screen at your desire and a smaller circle representing the joystick that can move only in the blue circle region. The smaller circle will not pass the edge of the larger circle if it exceeds its radius and will snap back to the center once released. We will also display the angle and distance of the smaller circle from the center of the larger circle.

Prerequisites

  • Basic knowledge of SwiftUI and iOS development.

Getting Started

Create a new SwiftUI project in Xcode and open the ContentView.swift file.

Step 1: Define State and Gesture Variables

We define the necessary state and gesture variables in the ContentView struct. Add the following code snippet inside the ContentView struct:

@State private var location: CGPoint = .zero
@State private var innerCircleLocation: CGPoint = .zero
@GestureState private var fingerLocation: CGPoint? = nil
@GestureState private var startLocation: CGPoint? = nil
  • location represents the center position of the larger circle (blue circle).
  • innerCircleLocation represents the center position of the smaller circle (green circle).
  • fingerLocation tracks the current finger position during the drag gesture.
  • startLocation stores the starting location when dragging the larger circle.

Step 2: Define Gesture Modifiers

Next, we define two gesture modifiers: simpleDrag and fingerDrag. These modifiers handle the dragging gestures for the larger and smaller circles, respectively.

Add the following code snippet below the gesture variables:

private let bigCircleRadius: CGFloat = 100 // Adjust the radius of the blue circle

var simpleDrag: some Gesture {
DragGesture()
.onChanged { value in
// Update the location based on the translation of the gesture
var newLocation = startLocation ?? location
newLocation.x += value.translation.width
newLocation.y += value.translation.height

// Calculate the distance between the center of the blue circle and the new location
let distance = sqrt(pow(newLocation.x - location.x, 2) + pow(newLocation.y - location.y, 2))

// Clamp the new location if it exceeds the radius of the blue circle
if distance > bigCircleRadius {
let angle = atan2(newLocation.y - location.y, newLocation.x - location.x)
newLocation.x = location.x + cos(angle) * bigCircleRadius
newLocation.y = location.y + sin(angle) * bigCircleRadius
}

self.location = newLocation
self.innerCircleLocation = newLocation // Update the green circle location
}
.updating($startLocation) { (value, startLocation, transaction) in
startLocation = startLocation ?? location
}
}

var fingerDrag: some Gesture {
DragGesture()
.onChanged { value in
// Calculate the distance between the finger location and the center of the blue circle
let distance = sqrt(pow(value.location.x - location.x, 2) + pow(value.location.y - location.y, 2))

// Calculate the angle between the center of the blue circle and the finger location
let angle = atan2(value.location.y - location.y, value.location.x - location.x)

// Calculate the maximum allowable distance within the blue circle
let maxDistance = bigCircleRadius

// Clamp the distance within the blue circle
let clampedDistance = min(distance, maxDistance)

// Calculate the new location at the edge of the blue circle
let newX = location.x + cos(angle) * clampedDistance
let newY = location.y + sin(angle) * clampedDistance

innerCircleLocation = CGPoint(x: newX, y: newY)
}
.updating($fingerLocation) { (value, fingerLocation, transaction) in
fingerLocation = value.location
}
.onEnded { value in
// Snap the smaller circle to the center of the larger circle
let center = location
innerCircleLocation = center
}
}
  • simpleDrag handles the dragging gesture for the larger circle. It updates the location based on the gesture’s translation and clamps the new location if it exceeds the radius of the blue circle.
  • fingerDrag handles the dragging gesture for the smaller circle. It calculates the distance and angle between the finger’s location and the center of the blue circle. It clamps the distance within the blue circle and calculates the new location at the edge of the blue circle. The onEnded closure snaps the smaller circle to the center of the larger circle.

Step 3: Add Circle Views and Text

Now, we can add the circle views and text to display the angle and distance information. Update the body property of the ContentView struct with the following code:

var body: some View {
ZStack {
// Larger circle (blue circle)
Circle()
.foregroundColor(.blue)
.frame(width: bigCircleRadius * 2, height: bigCircleRadius * 2)
.position(location)
.gesture(simpleDrag)

// Smaller circle (green circle)
Circle()
.foregroundColor(.green)
.frame(width: 50, height: 50)
.position(innerCircleLocation)
.gesture(fingerDrag)

// Angle text
Text(angleText)
.font(.title)
.foregroundColor(.white)
.bold()
.padding()
.background(Color.black.opacity(0.7))
.cornerRadius(10)
.position(x: UIScreen.main.bounds.width / 2, y: 50)
}
}
  • The larger circle (blue circle) is defined using the Circle() view. We set its position to location and attach the simpleDrag gesture to enable dragging.
  • The smaller circle (green circle) is defined similarly, using the innerCircleLocation as its position and the fingerDrag gesture.
  • The angle text is displayed using the Text view. We set its content to the angleText computed property, which calculates the angle in degrees. We style the text and position it at the top center of the screen.

Step 4: Calculate the Angle in Degrees

Lastly, we need to add the angleText computed property that calculates the angle of the smaller circle from the center of the larger circle in degrees. Update the ContentView struct with the following code snippet:

var angleText: String {
let angle = atan2(innerCircleLocation.y - location.y, innerCircleLocation.x - location.x)
var degrees = Int(-angle * 180 / .pi)

// Convert the degrees to a positive value
if degrees < 0 {
degrees += 360
}

return "(degrees)°"
}
  • The angleText property uses the atan2 function to calculate the angle in radians between the innerCircleLocation and the location. It then converts the angle to degrees and ensures it is in the range of 0 to 360.

Conclusion

You have successfully created a joystick-like control in SwiftUI! The control allows you to drag a smaller circle within the boundaries of a larger circle and displays the angle and distance information. You can further customize the appearance and behavior of the control to fit your specific needs.

Feel free to experiment with the code and enhance the control by adding additional features or animations. Happy coding!


Creating a Joystick Control in SwiftUI was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.

Previous Post
Next Post

Recent Posts

  • US imposes new rules to curb semiconductor design software sales to China
  • Grocery platform Misfits Market acquires The Rounds to further its mission of reducing food waste
  • SEC drops Binance lawsuit in yet another gift to crypto
  • G2 Speech Launches AI Digital Assistant
  • Google fixes bug that led AI Overviews to say it’s now 2024

Categories

  • Industry News
  • Programming
  • RSS Fetched Articles
  • Uncategorized

Archives

  • May 2025
  • April 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023

Tap into the power of Microservices, MVC Architecture, Cloud, Containers, UML, and Scrum methodologies to bolster your project planning, execution, and application development processes.

Solutions

  • IT Consultation
  • Agile Transformation
  • Software Development
  • DevOps & CI/CD

Regions Covered

  • Montreal
  • New York
  • Paris
  • Mauritius
  • Abidjan
  • Dakar

Subscribe to Newsletter

Join our monthly newsletter subscribers to get the latest news and insights.

© Copyright 2023. All Rights Reserved by Soatdev IT Consulting Inc.