Some apps use an onboarding flow to take users through some of the features as well as help get things set up, such as permissions. In this tutorial we’ll create a simple onboarding experience using the TabView and PageTabViewStyle so that your users can swipe through the introduction screens.
We will also make use of @AppStorage to store that the user has seen the onboarding screens and doesn’t need to see them again.
The first step is to create a new project. Just create a regular app that uses SwiftUI. You do not need to add Testing or Storage to the app, so leave them unchecked.
struct OnboardingView: View {
var body: some View {
TabView {
OnboardingPage(title: "Welcome to MyApp", description: "Your journey starts here!", imageName: "star")
OnboardingPage(title: "Stay Organized", description: "Track your tasks efficiently.", imageName: "checkmark.circle")
OnboardingPage(title: "Get Started", description: "Tap below to begin!", imageName: "arrow.right.circle")
}
.tabViewStyle(PageTabViewStyle())
}
}
struct OnboardingPage: View {
let title: String
let description: String
let imageName: String
var body: some View {
VStack(spacing: 20) {
Image(systemName: imageName)
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.foregroundColor(.blue)
Text(title)
.font(.title)
.bold()
Text(description)
.multilineTextAlignment(.center)
.padding()
}
.padding()
}
}
#Preview {
OnboardingView()
}
We begin by creating our OnboardingView that will be home to the TabView. In the TabView we make a call to an OnboardingPage. You don’t need to use the same OnboardingPage for each tab, but in this first example, we have for simplicity.
After that, we set the style of the TabView to PageTabViewStyle.
In the calls to the OnboardingPage, we pass in the three properties of title, description, and imageName that it has available to use.
The OnboardingPage struct contains the three properties mentioned and uses a VStack with three views within, the first being the Image view, and the latter two being Text views.
If you have added the Preview, you should now have a TabView that you can swipe through to see the pages we added.
Lets add a button to dismiss the Onboarding view.
struct OnboardingView: View {
@Binding var showOnboarding: Bool
var body: some View {
TabView {
OnboardingPage(title: "Welcome to MyApp", description: "Your journey starts here!", imageName: "star")
OnboardingPage(title: "Stay Organized", description: "Track your tasks efficiently.", imageName: "checkmark.circle")
VStack {
OnboardingPage(title: "Get Started", description: "Tap below to begin!", imageName: "arrow.right.circle")
Button(action: {
showOnboarding = false
}) {
Text("Get Started")
.font(.headline)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
.padding(.horizontal, 40)
}
}
.padding()
}
.tabViewStyle(PageTabViewStyle())
}
}
Changes in here include adding an @Binding propery wrapper on line 2 that will be used to let the view know if it should be visible. @Binding is a property wrapper that lets a child modify state that is owned by the parent. This allows it to have a single source of truth. In this example, OnboardingView does not own showOnboarding. Its parent will own it, and we allow this view to modify it.
We nest the last of the three OnboardingPage tabs in a VStack and then add a Button view on lines 11 – 22. We will use this to set showOnboarding to false so that it doesn’t show again.
When adding this, you will see complaints in Xcode about the Preview. Change it to this for now:
OnboardingView(showOnboarding: .constant(true))
It expects a boolean to be passed in, and because we can’t give that a default value in the view, we set it this way here.
We can now make use of this in ContentView:
struct ContentView: View {
@AppStorage("showOnboarding") private var showOnboarding = true
var body: some View {
if showOnboarding {
OnboardingView(showOnboarding: $showOnboarding)
} else {
Text("Main App Content")
}
}
}
Here we add in @AppStorage and call it “showOnboarding”. We default this to true. If showOnboarding is already set, then it doesn’t set the default value meaning that if we set this to false in our child view, then a relaunch would not reset it back to true. This only takes true as a value if showOnboarding doesn’t already exist in the @AppStorage.
In our view, we use some simple logic to decide which view to launch, either the OnboardingView or something else.
If you run all of this together on a device or in the simulator, you’ll now be able to dismiss the Onboarding and not see it again.
You might choose to provide an option in a settings menu where they can reset this to show again, or you might overwrite the AppStorage with a true if you launch a new version of the app.
We have many options in our Tabs. Some apps let users know why they are asking for permission for location, and when you click next or swipe next, the default permission screen is made visible. You might also use this to gather information about your user. If you have a weight tracking app, you might use onboarding to find out more about your users goals and so forth.
Any questions, please comment below.
Leave a Reply
You must be logged in to post a comment.