Configurable animated onboarding screen written programmatically in Swift for UIKit

Overview

UIOnboarding

UIOnboarding Title Page

GitHub license Twitter: @lascic

Configurable animated onboarding screen written programmatically in Swift for UIKit – inspired by many Apple-designed user interfaces in iOS – with Insignia as an example.

Designed for iPhone and iPod touch running iOS 13 or higher. Portrait mode only. Supports Dynamic Type, VoiceOver and Reduce Motion.

Table of Contents

Previews

Default 6.5" Default 4"
UIOnboarding Preview 6.5 inch

Accessibility

Dynamic Type VoiceOver Reduce Motion
UIOnboarding Preview Dynamic Type UIOnboarding Preview VoiceOver UIOnboarding Preview Redcue Motion

Installation

Swift Package Manager

To install UIOnboarding as a package, add https://github.com/lascic/UIOnboarding.git in the package manager in Xcode (under File/Add Packages...). Select the version from 1.0.0 or the main branch.

.package(url: "https://github.com/lascic/UIOnboarding.git", from: "1.0.0")
// or
.package(url: "https://github.com/lascic/UIOnboarding.git", branch: "main")

Demo Project Download

There is a demo project with and without SPM in the Demo directory: Demo/UIOnboarding Demo and Demo/UIOnboarding Demo SPM. This folder also provides an example for using it in a SwiftUI app: Demo/UIOnboarding SwiftUI.

You can download them as a .zip file to run it on a physical device or simulator in Xcode.

Before building and running the project, make sure to set it up with your own provisioning profile.

Usage

Setting up the UIOnboardingViewController takes a UIOnboardingViewConfiguration as the parameter.

UIKit

Make sure the view controller you're presenting from is embedded in a UINavigationController. OnboardingViewController is presented as a full screen view.

//In the view controller you're presenting
import UIOnboarding

let onboardingController: UIOnboardingViewController = .init(withConfiguration: .setUp())
onboardingController.delegate = self
navigationController?.present(onboardingController, animated: false)

Dismiss the onboarding view with the provided delegate method.

extension ViewController: UIOnboardingViewControllerDelegate {
    func didFinishOnboarding(onboardingViewController: UIOnboardingViewController) {
        onboardingViewController.modalTransitionStyle = .crossDissolve
        onboardingViewController.dismiss(animated: true, completion: nil)
    }
}

SwiftUI

As UIOnboardingViewController is a UIKit view controller, we rely on SwiftUI's UIViewControllerRepresentable protocol to make it behave as a View.

Create a UIOnboardingView struct which implements this protocol and use the .fullScreenCover() modifier introduced in iOS 14 to show it in your SwiftUI view you're presenting from.

Note that we assign SwiftUI's coordinator as the delegate object for our onboarding view controller.

onboardingController.delegate = context.coordinator
// In OnboardingView.swift
import SwiftUI
import UIOnboarding

struct UIOnboardingView: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIOnboardingViewController

    func makeUIViewController(context: Context) -> UIOnboardingViewController {
        let onboardingController: UIOnboardingViewController = .init(withConfiguration: .setUp())
        onboardingController.delegate = context.coordinator
        return onboardingController
    }
    
    func updateUIViewController(_ uiViewController: UIOnboardingViewController, context: Context) {}
    
    class Coordinator: NSObject, UIOnboardingViewControllerDelegate {
        func didFinishOnboarding(onboardingViewController: UIOnboardingViewController) {
            onboardingViewController.dismiss(animated: true, completion: nil)
        }
    }

    func makeCoordinator() -> Coordinator {
        return .init()
    }
}
// In ContentView.swift
.fullScreenCover(isPresented: $showingOnboarding, content: {
    UIOnboardingView()
        .edgesIgnoringSafeArea(.all)
})

Configuration

UIOnboardingViewConfiguration consists of five components.

  1. App Icon as UIImage
  2. Welcome Title as NSMutableAttributedString
  3. Core Features as Array
  4. Notice Text as UIOnboardingTextViewConfiguration (e.g. Privacy Policy, Terms of Service)
  5. Continuation Title as UIOnboardingButtonConfiguration

In a helper struct UIOnboardingHelper we define these components and combine them in an extension of UIOnboardingViewConfiguration.

Example

NSMutableAttributedString { let welcomeText: NSMutableAttributedString = .init(string: "Welcome to \n", attributes: [.foregroundColor: UIColor.label]), appNameText: NSMutableAttributedString = .init(string: Bundle.main.displayName ?? "Insignia", attributes: [.foregroundColor: UIColor.init(named: "camou")!]) welcomeText.append(appNameText) return welcomeText } //Core Features static func setUpFeatures() -> Array { return .init([ .init(icon: .init(named: "feature-1")!, title: "Search until found", description: "Over a hundred insignia of the Swiss Armed Forces – each redesigned from the ground up."), .init(icon: .init(named: "feature-2")!, title: "Enlist prepared", description: "Practice with the app and pass the rank test on the first run."), .init(icon: .init(named: "feature-3")!, title: "#teamarmee", description: "Add name tags of your comrades or cadre. Insignia automatically keeps every name tag you create in iCloud.") ]) } //Notice Text static func setUpNotice() -> UIOnboardingTextViewConfiguration { return .init(icon: .init(named: "onboarding-notice-icon")!, text: "Developed and designed for members of the Swiss Armed Forces.", linkTitle: "Learn more...", link: "https://www.lukmanascic.ch/portfolio/insignia", tint: .init(named: "camou")) } //Continuation Title static func setUpButton() -> UIOnboardingButtonConfiguration { return .init(title: "Continue", backgroundColor: .init(named: "camou")!) } }">
import UIOnboarding

struct UIOnboardingHelper {
    //App Icon
    static func setUpIcon() -> UIImage {
        return Bundle.main.appIcon ?? .init(named: "onboarding-icon")!
    }

    //Welcome Title
    static func setUpTitle() -> NSMutableAttributedString {
        let welcomeText: NSMutableAttributedString = .init(string: "Welcome to \n",
                                                           attributes: [.foregroundColor: UIColor.label]),
            appNameText: NSMutableAttributedString = .init(string: Bundle.main.displayName ?? "Insignia",
                                                           attributes: [.foregroundColor: UIColor.init(named: "camou")!])
        welcomeText.append(appNameText)
        return welcomeText
    }

    //Core Features
    static func setUpFeatures() -> Array
     {
        
    return .
    init([
            .
    init(
    icon: .
    init(
    named: 
    "feature-1")
    !,
                  
    title: 
    "Search until found",
                  
    description: 
    "Over a hundred insignia of the Swiss Armed Forces – each redesigned from the ground up."),
            .
    init(
    icon: .
    init(
    named: 
    "feature-2")
    !,
                  
    title: 
    "Enlist prepared",
                  
    description: 
    "Practice with the app and pass the rank test on the first run."),
            .
    init(
    icon: .
    init(
    named: 
    "feature-3")
    !,
                  
    title: 
    "#teamarmee",
                  
    description: 
    "Add name tags of your comrades or cadre. Insignia automatically keeps every name tag you create in iCloud.")
        ])
    }

    
    //Notice Text

        
    static 
    func 
    setUpNotice() 
    -> UIOnboardingTextViewConfiguration {
        
    return .
    init(
    icon: .
    init(
    named: 
    "onboarding-notice-icon")
    !,
                     
    text: 
    "Developed and designed for members of the Swiss Armed Forces.",
                     
    linkTitle: 
    "Learn more...",
                     
    link: 
    "https://www.lukmanascic.ch/portfolio/insignia",
                     
    tint: .
    init(
    named: 
    "camou"))
    }

    
    //Continuation Title

        
    static 
    func 
    setUpButton() 
    -> UIOnboardingButtonConfiguration {
        
    return .
    init(
    title: 
    "Continue",
                     
    backgroundColor: .
    init(
    named: 
    "camou")
    !)
    }
}
   

Extension

import UIOnboarding

extension UIOnboardingViewConfiguration {
    //UIOnboardingViewController init
    static func setUp() -> UIOnboardingViewConfiguration {
        return .init(appIcon: UIOnboardingHelper.setUpIcon(),
                     welcomeTitle: UIOnboardingHelper.setUpTitle(),
                     features: UIOnboardingHelper.setUpFeatures(),
                     textViewConfiguration: UIOnboardingHelper.setUpNotice(),
                     buttonConfiguration: UIOnboardingHelper.setUpButton())
    }
}

Moodboard

UIOnboarding First Moodboard UIOnboarding Second Moodboard

License

This project is MIT licensed.

Icon Usage Rights

Some in-app assets provided for this demo project are part of Insignia.

© 2021 Copyright Lukman Aščić. All rights reserved.

Links

Swiss Armed Forces Insignia from the App Store: https://apps.apple.com/ch/app/abzeichen/id1551002238
Author Website: https://lukmanascic.ch

Contributions

Contributions to UIOnboarding are more than welcome! Please file an issue or submit a pull request.

Comments
  • Fixed infinite loop when the view appears

    Fixed infinite loop when the view appears

    I've noticed a very high CPU usage when the view appears and found an infinite loop:

    graph LR
    id1("viewDidLayoutSubviews()")
    id2("updateUI()")
    id3("view.layoutIfNeeded()")
    
    id1-->id2
    id2-->id3
    id3-->id1
    
    opened by FelixLisczyk 5
  • Compile Error

    Compile Error

    I added the pkg with SPM and import the library. My code has a small stub which runs the UIOnboarding startup sequence:

        func doOnBoarding() -> Void {
            setUp()
            presentOnboarding()
        }
        
        @objc private func presentOnboarding() {
            let onboardingController: UIOnboardingViewController = .init(withConfiguration: .setUp())
            onboardingController.delegate = self
            navigationController?.present(onboardingController, animated: false)
        }
    
    

    I get Error "Type 'UIOnboardingViewConfiguration' has no member 'setUp'". in the .init(withConfiguration: .setUp() ) Any thoughts?

    opened by 224XS 3
  • Image in notice area too big

    Image in notice area too big

    Hey,

    I noticed, that when you put an image into the notice area, it doesn't get properly resized and is way too big. The images next to the bullet points get resized to fit, but the notice image is shown as is.

    The only fix right now is manually downsizing the image, but it should work as it does with all other images.

    Example:

    Simulator Screen Shot - iPhone 13 - 2022-07-22 at 14 00 24

    opened by hensch7 2
  • Allow dynamic font size scaling in title label

    Allow dynamic font size scaling in title label

    The title label currently uses a fixed font size:

    https://github.com/lascic/UIOnboarding/blob/413b98df2185f5bbc5ac21c3b7e926a109ec18f4/Sources/UIOnboarding/Views/UIOnboardingTitle.swift#L12

    This causes the layout to break with long app names and localization. For example, replace "Welcome to" with "Willkommen bei" and run the sample project on a small iPhone.

    UILabel supports automatic text scaling by setting the adjustsFontSizeToFitWidth property to true and specifying a minimumScaleFactor. Unfortunately, this only works with single-line text.

    My suggestion would be to split the title into two labels ("Welcome to" and app name), enable automatic text scaling for both, and place them inside a UIStackView.

    I can create a PR with this change, but would like to hear your opinion first. @lascic.

    bug 
    opened by FelixLisczyk 2
  • Multiple Constrains / Layout Failure in iPad (12.9)

    Multiple Constrains / Layout Failure in iPad (12.9)

    iPhone layout is perfect, however, in iPad runtime, there are multiple conflicts:

    1. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "<NSLayoutConstraint:0x60000031d180 UIOnboarding.UIOnboardingTitleLabel:0x7f7f5428a020.width == 904 (active)>", "<NSLayoutConstraint:0x600000316e40 UIOnboarding.UIOnboardingStack:0x7f7f542854b0.width == 480 (active)>", "<NSLayoutConstraint:0x60000032ec10 'UISV-alignment' UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.leading == UIOnboarding.UIOnboardingTitleLabel:0x7f7f5428a020.leading (active)>", "<NSLayoutConstraint:0x60000032ec60 'UISV-alignment' UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.trailing == UIOnboarding.UIOnboardingTitleLabel:0x7f7f5428a020.trailing (active)>", "<NSLayoutConstraint:0x60000032f2f0 'UISV-alignment' UIView:0x7f7f54247180.leading == UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.leading (active)>", "<NSLayoutConstraint:0x60000032eb20 'UISV-canvas-connection' UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.leading == UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.leading (active)>", "<NSLayoutConstraint:0x60000032ead0 'UISV-canvas-connection' H:[UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0]-(0)-| (active, names: '|':UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0 )>", "<NSLayoutConstraint:0x60000032ee90 'UISV-canvas-connection' UIOnboarding.UIOnboardingStack:0x7f7f542854b0.leading == UIView:0x7f7f54247180.leading (active)>", "<NSLayoutConstraint:0x60000032eee0 'UISV-canvas-connection' H:[_UILayoutSpacer:0x600001f5ce10'UISV-alignment-spanner']-(0)-| (active, names: '|':UIOnboarding.UIOnboardingStack:0x7f7f542854b0 )>", "<NSLayoutConstraint:0x60000032f160 'UISV-spanning-boundary' _UILayoutSpacer:0x600001f5ce10'UISV-alignment-spanner'.trailing >= UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.trailing (active)>" )

    Will attempt to recover by breaking constraint <NSLayoutConstraint:0x60000031d180 UIOnboarding.UIOnboardingTitleLabel:0x7f7f5428a020.width == 904 (active)>

    ( "<NSLayoutConstraint:0x600000316e40 UIOnboarding.UIOnboardingStack:0x7f7f542854b0.width == 480 (active)>", "<NSLayoutConstraint:0x60000031df90 UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.width == 904 (active)>", "<NSLayoutConstraint:0x60000032f2f0 'UISV-alignment' UIView:0x7f7f54247180.leading == UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.leading (active)>", "<NSLayoutConstraint:0x60000032eb20 'UISV-canvas-connection' UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.leading == UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.leading (active)>", "<NSLayoutConstraint:0x60000032ead0 'UISV-canvas-connection' H:[UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0]-(0)-| (active, names: '|':UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0 )>", "<NSLayoutConstraint:0x60000032ee90 'UISV-canvas-connection' UIOnboarding.UIOnboardingStack:0x7f7f542854b0.leading == UIView:0x7f7f54247180.leading (active)>", "<NSLayoutConstraint:0x60000032eee0 'UISV-canvas-connection' H:[_UILayoutSpacer:0x600001f5ce10'UISV-alignment-spanner']-(0)-| (active, names: '|':UIOnboarding.UIOnboardingStack:0x7f7f542854b0 )>", "<NSLayoutConstraint:0x60000032f160 'UISV-spanning-boundary' _UILayoutSpacer:0x600001f5ce10'UISV-alignment-spanner'.trailing >= UIOnboarding.UIOnboardingTitleLabelStack:0x7f7f542535a0.trailing (active)>" )

    Will attempt to recover by breaking constraint <NSLayoutConstraint:0x60000031df90 UIOnboarding.UIOnboardingTitleLabel:0x7f7f542a1eb0.width == 904 (active)>

    opened by 224XS 4
  • Enhancement: Adjust Font size

    Enhancement: Adjust Font size

    Hey there,

    I don't know how easily you could implement this, but it would be a very nice option to make the subtitle text of each bullet point a bit smaller. Currently, it's the same size as the title above it, just not bold. I'd love to see a size different or at least an option for it.

    If this is too much hassle due to the sizing adjustments necessary, I'd understand it.

    Thanks in advance P.S.: I love your repo, this Onboarding View is awesome!!

    opened by hensch7 1
Releases(2.0.0)
  • 2.0.0(Jun 21, 2022)

    UIOnboarding v2.0.0 improves support for localized welcome titles.

    Changes

    • Enable dynamic font size scaling for UIOnboardingTitleLabel. • Split the welcome title label into two separate UIOnboardingTitleLabel instances within a UIOnboardingTitleLabelStack for dynamic sizing. • The welcomeTitle parameter inside UIOnboardingViewConfiguration is now being separated into two parameters, allowing to specifically set the text for each title line label when needed. • These new parameters now have generalized names, firstTitleLine and secondTitleLine.

    Discussion

    If a localization only needs one title line (e.g. in another language), you can leave either one of the two new parameters as an empty string. The new title label stack view automatically adjusts its height.

    // First Title Line
    // App Name
    static func setUpFirstTitleLine() -> NSMutableAttributedString {
        return .init(string: Bundle.main.displayName ?? "Distintivos", attributes: [
                .foregroundColor: UIColor.init(named: "camou")!
        ])
    }
    
    // Second Title Line
    // Empty
    static func setUpSecondTitleLine() -> NSMutableAttributedString {
        return .init(string: "")
    }
    

    Credits

    I'd like to thank Felix Lisczyk, @FelixLisczyk, for his contribution.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.3(May 12, 2022)

    Version 1.1.3 addresses a crucial performance issue.

    This has been fixed with improved conditionals within interface environment changes.

    Improvements

    • Fix infinite loop caused by updateUI() on viewDidLayoutSubviews()

    Credits

    I'd like to thank Felix Lisczyk, @FelixLisczyk, for his contribution.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.2(May 4, 2022)

    Improvements

    • Continuous corner curves • Enable dynamic pointer effect on continue button • Improve support for Mac Catalyst

    Credits

    I'd like to thank Ethan Lipnik, @EthanLipnik, for his contribution.

    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Apr 30, 2022)

  • 1.1.0(May 13, 2022)

    • Add iPad support for all size classes and interface orientations, including Split View and Slide Over.

    Improvements

    • Improve continue button size adaptivity based on user's Dynamic Type setting. • Separate feature cell label into two labels for better distinction in VoiceOver. • Animate bottom blur view in and out based on scroll position. • Fix scroll view indicator inset issue.

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Feb 19, 2022)

Owner
Lukman “Luke” Aščić
Interaction Design student at the Zurich University of the Arts
Lukman “Luke” Aščić
An animated popover that pops out a given frame, great for subtle UI tips and onboarding.

Animated popover that pops out of a frame. You can specify the direction of the popover and the arrow that points to its origin. Color, border radius

Andrea Mazzini 3k Jan 8, 2023
DeliveryOnboardingSwiftUI - A Delivery Onboarding screen made with SwiftUI

DeliveryOnboardingSwiftUI Its a Onboarding screen made with SwiftUI

null 1 Feb 5, 2022
OnboardKit - Customizable user onboarding for your UIKit app in Swift

OnboardKit Customizable user onboarding for your UIKit app in Swift Requirements Swift 5.0 Xcode 10 iOS 11.0+ Installation Carthage github "NikolaKire

Nikola Kirev 470 Dec 23, 2022
iOS library Paper Onboarding is a material design UI slider written on Swift.

iOS library Paper Onboarding is a material design UI slider written on Swift. We specialize in the designing and coding of custom UI

Ramotion 3.2k Jan 5, 2023
A Swift Recreation of Attach-Detach, with some configurable options

Attach-Detach-Sw A Swift Recreation of Attach-Detach, with some configurable options Usage To use, you'll need to specify if you are attaching or deta

Serena 1 Dec 24, 2021
SwiftUI library for a walkthrough or onboarding flow with tap actions

Concentric Onboarding iOS library for a walkthrough or onboarding flow with tap actions written with SwiftUI We are a development agency building phen

Exyte 955 Jan 4, 2023
A swifty iOS framework that allows developers to create beautiful onboarding experiences.

SwiftyOnboard is being sponsored by the following tool; please help to support us by taking a look and signing up to a free trial SwiftyOnboard A simp

Juan Pablo Fernandez 1.2k Jan 2, 2023
An iOS framework to easily create a beautiful and engaging onboarding experience with only a few lines of code.

Onboard Click Here For More Examples Important Onboard is no longer under active development, and as such if you create any issues or submit pull requ

Mike 6.5k Dec 17, 2022
An iOS framework to easily create simple animated walkthrough, written in Swift.

Intro Overview An iOS framework to easily create simple animated walkthrough, written in Swift. Requirements iOS8 Installation with CocoaPods Intro is

Nurdaulet Bolatov 33 Oct 1, 2021
Presentation helps you to make tutorials, release notes and animated pages.

Presentation helps you to make tutorials, release notes and animated pages.

HyperRedink 3k Jan 5, 2023
A simple keyframe-based animation framework for UIKit. Perfect for scrolling app intros.

Jazz Hands is a simple keyframe-based animation framework for UIKit. Animations can be controlled via gestures, scroll views, KVO, or ReactiveCocoa. J

IFTTT 6.4k Dec 28, 2022
SwiftyWalkthrough is a library for creating great walkthrough experiences in your apps, written in Swift.

SwiftyWalkthrough is a library for creating great walkthrough experiences in your apps, written in Swift. You can use the library to allow users to navigate and explore your app, step by step, in a predefined way controlled by you.

Rui Costa 370 Nov 24, 2022
A simple keyframe-based animation framework for iOS, written in Swift. Perfect for scrolling app intros.

RazzleDazzle is a simple AutoLayout-friendly keyframe animation framework for iOS, written in Swift. Perfect for scrolling app intros. RazzleDazzle gr

IFTTT 3.4k Jan 1, 2023
Simple coach mark library written in Swift

Minamo Simple coach mark library written in Swift Usage Initialize let rippeleView = RippleView() rippeleView.tintColor = UIColor(red: 0.3, green: 0.7

yukiasai 248 Nov 24, 2022
Create walkthroughs and guided tours (coach marks) in a simple way, with Swift.

Add customizable coach marks into your iOS project. Available for both iPhone and iPad. ⚠️ Instructions 2.0.1 brings a couple of breaking changes, ple

Frédéric Maquin 4.9k Jan 3, 2023
Fully customisable tooltip view in Swift for iOS.

Description EasyTipView is a fully customizable tooltip view written in Swift that can be used as a call to action or informative tip. Contents Featur

Teo 2.9k Dec 27, 2022
Swift based simple information view with pointed arrow.

InfoView View to show small text information blocks with arrow pointed to another view.In most cases it will be a button that was pressed. Example To

Anatoliy Voropay 60 Feb 4, 2022
Swift Actors Introduction

Swift-Actors-Introduction Swift 5.5~ 並行処理におけるデータ整合やその他の不具合を防ぐための仕組み。 https://doc

Sho Emoto 0 Jan 3, 2022
ColorMix-by-IUKit - colorMix app by Intro to app development in Swift

colorMix-by-IUKit colorMix app by "Intro to app development in Swift" In this ap

null 0 Feb 11, 2022