arrow-left BACK TO ALL
SwiftUI tutorial: replicating the Apple Card application
This article is the second one in the Replicating series where we recreate UI elements of high-quality apps. Here we take a look at Apple Card app.

Apple’s new declarative interface build tool SwiftUI is changing a lot in each new release, but what’s available now is already a versatile and powerful system. And while simple UI forms are straight-forward to implement, some of the more complex screens can require quite a bit of work, especially given the somewhat limited API documentation and lack of examples.

So, continuing our tutorial series of replicating complex UI screens, here’s how to implement one of the screens in one of Apple’s application - Apple Card.

That is the original. To see what we came up with, skip to the bottom of the page.

Disclaimer about this SwiftUI tutorial

We had a fair bit of problems while creating this tutorial for a number of reasons:

  • SwiftUI still in beta with the official release coming soon.
  • Our developer had problems with using preview canvas in Xcode - it was crashing even after he updated to the latest Xcode and OS versions.
  • We have limited access to the app in our region, so we had to rely on public videos and animations (please excuse any discrepancies).
  • Same goes for the design - insets, background colors, fonts etc. had to be chosen by eye. However, it doesn’t affect the SwiftUI parts of this tutorial.
  • We could not find a way to create the curved text (the one following a circular path) using pure SwiftUI. If you know a way to do it, please let us know in the comments.

So, let's begin. We’ve separated the code into snippets by functionality, and will do our best to highlight the interesting parts.

UI Stubs

For our screen, we need to implement basic things:

SwiftUI Navigation view

Body:

Labels:

We added top labels as titles for simplicity, the same reason we use static values for them.

Thankfully, the code for adding buttons is self-explanatory:

Preparing data for Circle Control

Let's move on to Circle Control:

Our circle control will receive data for calculating the values and displaying them to users. We will call this data object “Segment”.

We then create an object of the newly declared type to represent the currently selected segment.

We want the bottom descriptions to depend on the selected segment from the circle control, so we add the @State attribute. Simply put, the @State annotation denotes a changeable value that has to be updated in the UI. Here’s Apple’s documentation entry about it. We use this @State variable in the following block of code to set the values:

The following properties are also necessary for CircleControl: total balance, current value and the knowledge of the currently selected segment.

The selected segment is denoted with the @Binding annotation, which basically says that it comes from outside the view domain. After that, the code for control creation will look something like this:

Circle Control UI

Here’s the body initialization code:

As you can see, the bulk of the UI is implemented using circles and arcs. Let’s look closer at 3 similar methods - createProgressArc, createPoint and createControlPoint. Also, we shall define some auxiliary methods:

  • for finding current segment
  • for finding nearest segment point (for the snapping effect)

For the progress arc, value points and the control point methods we need to calculate the correct angle on the circle. The value of this angle is the position divided by the totalBalance, multiplied by 2 * pi.

And for translating this angle to the top position of the circle, we should reduce the value by pi /2.

angle = valuePosition / totalBalance * 2 * .pi - .pi / 2

Finally, we need to calculate the x and y points for the location.

The most important part of this control is the movement control point, and we want it to animate smoothly. The control point itself is simple view, but with an added drag gesture (similar to UIPanGestureRecognizer). This is how we add it:

Calculating values for Circular Control

When we need to find the new value, we calculate it as the ratio of the new angle to 2 * pi * total balance, so here are the necessary steps:

  • Get a vector from the drag location and the center of our circle (value of the center is the same as the radius in this case).
  • Get the angle value.
  • Apply a fix for negative angle values.
  • Calculate the new value.

Implementing snapping to points

If during the control point movement we detect a segment point nearby (using some arbitrary coefficient), we “snap” to it - simply setting the value to the found segment point.

Otherwise we just change the value linearly, but make sure we stay within the maximum and minimum circle values.

Which’s great is since the current value is a @State variable, the UI will update automatically when the value is changed because it’s used for drawing the control point and the progress arc.

That is our replica.

And there it is!

The final code is clean and easy to follow, but was not trivial to implement because of some new concepts SwiftUI presents.

Hopefully it’s useful to someone else who decides to create custom UI screens using SwiftUI. The full project is available on GitHub. If you'd like to work with us, drop us a line here.

// Keep reading
icon
iOS Department • 10 Feb 2020
Creating haptic feedback in iOS 13 with Core Haptics
With iOS 13 release Apple finally added a new framework called Core Haptics, which allows developers to define and play custom haptic feedback patterns.
icon
iOS Department • 2 Oct 2019
SwiftUI tutorial for slider control
We tried out SwiftUI for creating a liquid-like motion graphics with spring animation. We chose a flow of several sequential screens as our canvas.
icon
iOS Department • 23 Dec 2019
SwiftUI tutorial: replicating the Activity application
This article is the third one in the Replicating series where we recreate UI elements of high-quality apps. Here we take a look at Apple’s Activity app.
sent image
Thank you for
contacting us!
Your request has been sent, please wait for a response.
Send a letter