OnBoarding View with SwiftUI

Krupanshu Sharma
6 min readFeb 7, 2023

--

Onboarding screens are a critical part of any mobile app’s user experience.

They provide an introduction to the application and its core features, guide users through key functions, and help them become familiar with the app quickly.

In this blog post, we’ll look at how you can use SwiftUI to create beautiful onboarding screens with minimal effort.

This is exactly what we are going to implement.

As you can see, we need to design a screen which contain a background image, a image which you can use to set content, Title, Subtitle, Button and paging.

It’s a very small part of a application. But code reusability will always be the best practices whenever we develop anything.

Create a sample application.

Lets create a Model which we are going to use.

Model

Lets create a filename FruitModel and add below code.

import Foundation
import SwiftUI

// MARK: - FRUITS DATA MODEL

struct Fruit: Identifiable {
var id = UUID()
var title: String
var headline: String
var image: String
var gradientColors: [Color]
}

Here we created a FRUITS DATA MODEL.

DummyData

Let’s create some dummy data which we are going to use further in application.

import Foundation
import SwiftUI

// MARK: - FRUITS DATA

let fruitsData: [Fruit] = [
Fruit(
title: "Blueberry",
headline: "Blueberries are sweet, nutritious and wildly popular fruit all over the world.",
image: "blueberry",
gradientColors: [Color("ColorBlueberryLight"), Color("ColorBlueberryDark")]
),
Fruit(
title: "Strawberry",
headline: "Widely appreciated for its characteristic aroma, red color, juicy texture, and sweetness.",
image: "strawberry",
gradientColors: [Color("ColorStrawberryLight"), Color("ColorStrawberryDark")]
),
Fruit(
title: "Lemon",
headline: "There's no doubt lemons are delicious, but does adding them to water make you healthier?",
image: "lemon",
gradientColors: [Color("ColorLemonLight"), Color("ColorLemonDark")]
),
Fruit(
title: "Plum",
headline: "Plums are a very nutritious fruit. An excellent source of vitamins, minerals, fiber and antioxidants.",
image: "plum",
gradientColors: [Color("ColorPlumLight"), Color("ColorPlumDark")]
),
Fruit(
title: "Lime",
headline: "Sour, round, and bright green citrus fruits. Limes are high in vitamin C, antioxidants, and other nutrients.",
image: "lime",
gradientColors: [Color("ColorLimeLight"), Color("ColorLimeDark")]
),
Fruit(
title: "Pomegranate",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "pomegranate",
gradientColors: [Color("ColorPomegranateLight"), Color("ColorPomegranateDark")]
),
Fruit(
title: "Pear",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "pear",
gradientColors: [Color("ColorPearLight"), Color("ColorPearDark")]
),
Fruit(
title: "Gooseberry",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "gooseberry",
gradientColors: [Color("ColorGooseberryLight"), Color("ColorGooseberryDark")]
),
Fruit(
title: "Mango",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "mango",
gradientColors: [Color("ColorMangoLight"), Color("ColorMangoDark")]
),
Fruit(
title: "Watermelon",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "watermelon",
gradientColors: [Color("ColorWatermelonLight"), Color("ColorWatermelonDark")]
),
Fruit(
title: "Cherry",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "cherry",
gradientColors: [Color("ColorCherryLight"), Color("ColorCherryDark")]
),
Fruit(
title: "Grapefruit",
headline: "Sweet, bell-shaped fruits that have been enjoyed since ancient times. They can be eaten crisp or soft.",
image: "grapefruit",
gradientColors: [Color("ColorGrapefruitLight"), Color("ColorGrapefruitDark")]
),
Fruit(
title: "Apple",
headline: "Apples are one of the most popular, and exceptionally healthy fruit for good reason.",
image: "apple",
gradientColors: [Color("ColorAppleLight"), Color("ColorAppleDark")]
)
]

I have already added some colors and fruits already in project. And I am using those to create dummy data.

Lets create the card-view which will show our onboard content.

FruitCardView

import SwiftUI

struct FruitCardView: View {
// MARK: - PROPERTIES

var fruit: Fruit

@State private var isAnimating: Bool = false

// MARK: - BODY

var body: some View {
ZStack {
VStack(spacing: 20) {
// FRUIT: IMAGE
Image(fruit.image)
.resizable()
.scaledToFit()
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.15), radius: 8, x: 6, y: 8)
.scaleEffect(isAnimating ? 1.0 : 0.6)

// FRUIT: TITLE
Text(fruit.title)
.foregroundColor(Color.white)
.font(.largeTitle)
.fontWeight(.heavy)
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.15), radius: 2, x: 2, y: 2)

// FRUIT: HEADLINE
Text(fruit.headline)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.padding(.horizontal, 16)
.frame(maxWidth: 480)

// BUTTON: START
StartButtonView()
} //: VSTACK
} //: ZSTACK
.onAppear {
withAnimation(.easeOut(duration: 0.5)) {
isAnimating = true
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
.background(LinearGradient(gradient: Gradient(colors: fruit.gradientColors), startPoint: .top, endPoint: .bottom))
.cornerRadius(20)
.padding(.horizontal, 20)
}
}

struct FruitCardView_Previews: PreviewProvider {
static var previews: some View {
FruitCardView(fruit: fruitsData[0])
}
}
  1. Create a new SwiftUIView file, name it FruitCardView.
  2. Create fruit property. Which we are going to use to set our desired fruit content.
  3. In Body, Created Zstack. Creating VStack which will contain Image, title, headline and StartButton.
  4. onAppear, we are adding animation. added frame, background, and corner radius.
  5. Update the previews to see the preview of fruit from Dummy Data.
  6. We need to create StartButtonView. Here it is.
struct StartButtonView: View {
// MARK: - PROPERTIES

@AppStorage("isOnboarding") var isOnboarding: Bool?

// MARK: - BODY

var body: some View {
Button(action: {
isOnboarding = false
}) {
HStack(spacing: 8) {
Text("Start")

Image(systemName: "arrow.right.circle")
.imageScale(.large)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.background(
Capsule().strokeBorder(Color.white, lineWidth: 1.25)
)
} //: BUTTON
.accentColor(Color.white)
}
}

struct StartButtonView_Previews: PreviewProvider {
static var previews: some View {
StartButtonView()
.preferredColorScheme(.dark)
.previewLayout(.sizeThatFits)
}
}

SwiftUI has a dedicated property wrapper for reading values from UserDefaults, which will automatically reinvoke your view’s body property when the value changes. That is, this wrapper effectively watches a key in UserDefaults, and will refresh your UI if that key changes.

  1. we have defined @AppStorage for saving isOnboarding as a Bool value. By default, we will be setting it as true on launch time. and once user bypass the onboarding view, we are going to set it to False.
  2. We have created a Button, Which will be in shape of Capsule.

We have our raw views are ready. we are going to use them now.

OnBoardingView

import SwiftUI

struct OnBoardingView: View {
// MARK: - PROPERTIES

var fruits: [Fruit] = fruitsData

// MARK: - BODY

var body: some View {
TabView {
ForEach(fruits[0...5]) { item in
FruitCardView(fruit: item)
} //: LOOP
} //: TAB
.tabViewStyle(PageTabViewStyle())
.padding(.vertical, 20)
}

}

struct OnBoardingView_Previews: PreviewProvider {
static var previews: some View {
OnBoardingView()
}
}
  1. We have fruits property which is array of Fruit.
  2. We are going to use TabView.
  3. using loop, we are going to create FruitCardView. and set tabViewStyle to PageTabViewStyle

This is how your view will looks in preview.

Nice, Right ??

Now go your main app file and modify the code as below

@main
struct OnBoardingViewSwiftUIApp: App {
@AppStorage("isOnboarding") var isOnboarding: Bool = true

var body: some Scene {
WindowGroup {
if isOnboarding {
OnBoardingView()
} else {
ContentView()
}
}
}
}

We are using the AppStorage variable for show/hide of OnBoardingView

If isOnboarding is true, then show OnBoardingView else show contentView.

We are also modifying our contentView as below.

import SwiftUI

struct ContentView: View {
// MARK: - PROPERTIES

@AppStorage("isOnboarding") var isOnboarding: Bool?

var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")

Button(action: {
isOnboarding = true
}) {
HStack(spacing: 8) {
Text("Re-Start")

Image(systemName: "arrow.right.circle")
.imageScale(.large)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.background(
Capsule().strokeBorder(Color.black, lineWidth: 1.25)
)
} //: BUTTON
.accentColor(Color.black)
}
.padding()
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Here I have added one extra Re-Start button. which will set the isOnboarding to true again. This is to show your onBoardingView again if you have bypass it.

Let’s Run the app. Here is the end result.

Cool Right…

When it comes to onboarding screen design, SwiftUI offers a lot of flexibility. With SwiftUI, you can easily create custom views and controls. This means that you can tailor the look and feel of your onboarding screens to match your brand identity.

You can also use SwiftUI to lay out your content in a way that makes sense for your app’s user flow. And you do not need to be depend on any 3rd party libraries.

Wrap-Up

You can download the completed project from here. Source-Code.

Thanks for your time, That’s it for today.

Happy coding, and see you next time! :]

Wait….If you like what you read, be sure to 👏👏👏 below.

--

--