A hands-on overview of SwiftUI

Black bird flying through cellphone screens
Summary
  • The post demonstrates how to replicate a Tinder-like UI using SwiftUI, the new declarative UI framework Apple announced at WWDC 2019, which is compatible with all Apple platforms, built using Swift, and replaces Storyboards and AutoLayout.
  • The layout is broken into three main components: a TopStack with three horizontally aligned buttons, a CardView combining a UserImageView (with image, gradient overlay, and rounded corners) and UserInfoView (user name, age, profession), and a BottomStack with horizontally aligned buttons.
  • Key SwiftUI concepts covered include HStack, VStack, and ZStack for layout arrangement, Spacer() for filling gaps between stack items, overlays with LinearGradient, padding, shadows, and real-time previews via the PreviewProvider struct.
  • The final ContentView combines TopStack, CardView, and BottomStack vertically with spacing and padding to produce the complete Tinder-like interface.

Did you hear about the new UI Framework Apple announced at WWDC 2019? It’s called SwiftUI and it feels like magic.

Mac, iOS, iPad, WatchOS and tvOS developers are hyped!

Here’s why:

  • SwiftUI is compatible with all Apple platforms
  • SwiftUI is declarative
  • SwiftUI is not just a wrapper for UIKit — it’s new and built using Swift!
  • SwiftUI is not based on Storyboards! Hello, code review 🙂
  • SwiftUI replaces AutoLayout — everything is based on stack views now

That’s a lot, and since the whole community is already creating plenty of SwiftUI content, even writing books, I want to focus on a practical example of how to replicate a Tinder-like UI using this lovely framework. Are you ready?

First things first

When you create a new project using XCode 11 and check the SwiftUI box, it comes with a ContentView file:

import SwiftUI

Struct ContentView : {
  var body: some View {
    Text("Hello World")
  }
}

#if DEBUG
struct ContentView_Previews : Previewprovider {
  struct var previews: some View {
    ContentView()
  }
}
#endif

There are two important things to clear up here:

  • The first struct is where you write your view/layout code.
  • The second structure is where you declare what you want to preview. Finally, we have real-time previews!

We’ll build a few components and when you want to check what they look like, just change the ContentView() inside previews var by the component you want to preview.

This is the layout we want to achieve today:

 

A cellphone screen with a Tinder-like layout.

 

Cool right? It is a vertical layout composed of 3 important components:

  • The top component contains three horizontally aligned buttons.
  • Middle component is user image and info.
  • Bottom component contains five horizontally aligned buttons.

Top controls

We’re ready to create our first component! Since our interface has many buttons, we’ll start by writing a helper function that creates a button based on an image asset name.

func button(for icon: String) -> some View {
  Button(action: {}) {
    Image(icon)
      .renderingMode(.original)
  }
}

Cool! This is going to help us because our first view needs three different buttons aligned horizontally. Let’s name it TopStack:

struct TopStack : View {
  var body: some View {
    HStack {
      button(for: "top_left_profile")
      Spacer()
      button(for: "tinder")
      Spacer()
      button(for: "top_right_messages")
    }
  }
}

If you’re familiar with UIKit, everything looks good until you read Spacer(). Spacers can be used to fill the gap between stack items in order to fit the remaining view space. HStack should be used to build horizontal stack layouts — if you want to go vertical use VStack.

Here’s our first result:

A fire drawn on an application layout.

Bottom controls

We can do the same for the bottom controls, which contains five different buttons. Let’s name it BottomStack. Very similar to the first one isn’t it?

struct BottomStack : View {
    var body: some View {
      HStack {
        button(for: "refresh_circle")
        button(for: "dismiss_circle")
        button(for: "like_circle")
        button(for: "boost_circle")
      }
    }
  }

Five Tinder-like buttons.

Image, overlays, and gradients

Looking good! Let’s dive a bit deeper. Now we need to build the view between top and bottom controls, how should it look? Basically we want an image with rounded corners, but with one extra detail: if we want to show user info, like name and age, we need a small black gradient on the bottom. Our font color is white and the gradient allows us to see the user’s name if the image is bright. We can call that UserImageView.

struct UserImageView : View {
  var body: some View {
    Image("kaori")
      .resizable()
      .overlay (
        Rectangle()
          .fill (
            LinearGradient(gradient: Gradient(colors: [.cler, .black]),
                           startPoint: .center, endPoint: .bottom)
          )
          .clipped()
      )
      .cornerRadius(10, antialiased: true)
  }
}

So, a couple of new things here! Image…resizable? OK…Why? It’s because our image needs to fill the whole space between top and bottom controls!

What about overlay? We’re showing user info above the image, so we need to overlay the image with a gradient layer — we can use different shape forms as layers, such as Circles and Rectangles. For now, let’s build a rectangle and fill it with a gradient. Our gradient starts clear at the middle and finishes black at the bottom.

Check the results:

A cellphone screen with a Tinder-like layout.

User info

Now we want to show the user’s name, age, and profession above the image. This view is called UserInfoView — check the code below:

struct UserInfoView : View {
    var body: some View {
        VStack(alignment: .leading) {
            Spacer()
            Text("Kaori Miyazono, 30")
                .font(.title)
                .fontWeight(.regular)
                .color(.white)
            Text("Musician")
                .font(.system(size: 17))
                .fontWeight(.light)
                .color(.white)
            }
            .padding()
    }
}

This guy above here starts creating a vertical stack containing a Spacer and two Text components. We need the spacer again here because it’ll keep our user info on the bottom of the stack. We also want to add some padding to create some space from the view borders.

Time to combine UserImageView and UserInfoView!

Let’s create CardView, containing the user image and info:

struct CardView : View {
  var body: some View {
    ZStack(alignment: .leading) {
      UserImageView()
      UserInfoView()
    }
    .shadow(radius: 2)
  }
}

We want UserInfoView to be aboveUserImageView — easy to achieve on SwiftUI withZStack. We have used HStack and VStack for stacking views horizontally and vertically, ZStacks are used to pile views on top of each other. At the end of the file, you can see how easily you can add shadows using SwiftUI.

Here’s the result:

A cellphone screen with a Tinder-like layout.

The grand finale

Let’s combine the three components inside ContentView and get to the final layout:

struct ContentView : View {
  var body: some View {
    VStack(alignment: .center, spacing: 20) {
      TopStack()
        .padding(.horizontal)
      CardView()
        .padding(.horizontal, 10)
      BottomStack()
    }
    .padding(.bottom, 15)
  }
}

What do we have here? Basically, we are stacking the three components vertically, with a 20 height spacing between each. We also added some horizontal padding to TopStack and CardView, and finally a bottom respire for the VStack.

Final result

Inside theContentView file change your preview struct by ContentView() and we are done!

This is our beautiful layout made using SwiftUI:

A cellphone screen with a Tinder-like layout.

Source code

FAQ

What is SwiftUI?

SwiftUI is a new UI framework announced by Apple at WWDC 2019. It is compatible with all Apple platforms (Mac, iOS, iPad, WatchOS, and tvOS), is declarative, is built using Swift, is not just a wrapper for UIKit, is not based on Storyboards, and replaces AutoLayout with everything based on stack views.

What are HStack, VStack, and ZStack used for in SwiftUI?

HStack is used to build horizontal stack layouts, VStack is used for vertical stacks, and ZStack is used to pile views on top of each other. In the Tinder-like UI example, HStack arranges the top and bottom buttons horizontally, VStack arranges user info vertically, and ZStack places UserInfoView above UserImageView.

What is the purpose of Spacer() in SwiftUI?

Spacers are used to fill the gap between stack items in order to fit the remaining view space. In the example, Spacer() is used in TopStack to separate the three horizontal buttons and in UserInfoView to keep the user info on the bottom of the stack.

How do you preview views in SwiftUI?

When you create a new project using XCode 11 and check the SwiftUI box, it comes with a ContentView file containing two structs. The first struct is where you write your view/layout code, and the second structure is where you declare what you want to preview. To preview a specific component, change the ContentView() inside the previews var to the component you want to preview.

How is the gradient overlay on the user image created in the example?

The UserImageView uses an overlay with a Rectangle filled with a LinearGradient. The gradient uses colors from clear to black, with a startPoint of .center and endPoint of .bottom, allowing the user's name to be visible even if the image is bright. The image also uses .resizable() to fill the space and .cornerRadius(10) for rounded corners.

About the author.

David Doll
David Doll

A developer who loves usability, beautiful UI’s and sushi. Surf in the morning, games at night. If there is food, I’m in.