Swiftpack.co - lascic/UIOnboarding as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
See all packages published by lascic.
lascic/UIOnboarding 1.1.3
Apple-esque animated welcome screen for iOS and iPadOS. Configurable.
⭐️ 220
🕓 4 days ago
iOS
.package(url: "https://github.com/lascic/UIOnboarding.git", from: "1.1.3")

UIOnboarding

UIOnboarding Title Page

Configurable animated onboarding screen written programmatically in Swift for UIKit – inspired by the Apple Stocks app – with Insignia as an example.

Developed and designed by Lukman Aščić.

UIOnboarding supports iPhone, iPad and iPod touch running iOS and iPadOS 13 or higher, including core accessibility features such as Dynamic Type, VoiceOver, Reduce Motion for all devices and Split View and Slide Over for iPad.

Table of Contents

Previews

iPhone and iPod touch

Default 6.5" Default 4"
UIOnboarding Preview 6.5 inch

iPad

12.9" Portrait 12.9" Landscape
UIOnboarding Preview 12.9 inch Portrait UIOnboarding Preview 12.9 inch Landscape

Accessibility

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

Split View

1/3 iPad Landscape 1/2 iPad Landscape 2/3 iPad Landscape
UIOnboarding Slit View 1/3 Landscape UIOnboarding Slit View 1/2 Landscape UIOnboarding Slit View 2/3 Landscape
1/3 iPad Portrait 2/3 iPad Portrait
UIOnboarding Slit View 1/3 Portrait UIOnboarding Slit View 2/3 Portrait

Slide Over

iPad Portrait iPad Landscape
UIOnboarding Slide Over Portrait UIOnboarding Slide Over Landscape

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.1.3 or the main branch.

.package(url: "https://github.com/lascic/UIOnboarding.git", from: "1.1.3")
// 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

UIOnboardingViewController takes a UIOnboardingViewConfiguration parameter for setup.

UIKit

Make sure the view controller you're presenting from is embedded in a UINavigationController. OnboardingViewController has been set up to be presented as a full screen view.

// In the view controller you're presenting
import UIKit
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

We rely on SwiftUI's UIViewControllerRepresentable protocol to make the UIKit UIOnboardingViewController behave as a SwfitUI View.

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

.fullScreenCover(isPresented: $showingOnboarding, content: {
    OnboardingView.init()
        .edgesIgnoringSafeArea(.all)
}

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 OnboardingView: 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
import SwiftUI

struct ContentView: View {
    @State private var showingOnboarding = true
    
    var body: some View {
        NavigationView {
            Text("Hello, UIOnboarding!")
                .toolbar {
                    Button {
                        showingOnboarding = true
                    } label: {
                        Image(systemName: "repeat")
                    }
                }
                .fullScreenCover(isPresented: $showingOnboarding, content: {
                    OnboardingView.init()
                        .edgesIgnoringSafeArea(.all)
                })
        }
    }
}

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

Configuration

UIOnboardingViewConfiguration consists of five components.

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

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

Example

import UIKit
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<UIOnboardingFeature> {
        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())
    }
}

Further Setup

You may present the onboarding screen only once (on first app launch) with the help of a User Defaults flag. Note that an unspecified UserDefaults bool(forKey:) key is set to false by default.

if !UserDefaults.standard.bool(forKey: "hasCompletedOnboarding") {
    showOnboarding()
}

Toggle onboarding completion in the provided delegate method.

func didFinishOnboarding(onboardingViewController: OnboardingViewController) {
    onboardingViewController.modalTransitionStyle = .crossDissolve
    onboardingViewController.dismiss(animated: true) { 
        UserDefaults.standard.set(true, forKey: "hasCompletedOnboarding")
    }
}

Further Readings

Copyright

UIOnboarding is MIT licensed.

The Insignia app icon and Insignia feature cell assets are copyright Lukman Aščić. All rights reserved. None of these materials or parts of it may be reproduced or distributed by any means without prior written permission of the copyright owner.

GitHub

link
Stars: 220
Last commit: 4 days ago
jonrohan Something's broken? Yell at me @ptrpavlik. Praise and feedback (and money) is also welcome.

Release Notes

v1.1.3
4 days ago

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.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics