Add pages and page-controller for better onboarding (#690)

This commit is contained in:
Nathan Mattes 2022-11-26 15:22:23 +01:00
parent b3a4967c80
commit 831d5a6774
6 changed files with 535 additions and 575 deletions

View File

@ -115,6 +115,9 @@
D87BFC8F291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */; };
D8916DC029211BE500124085 /* ContentSizedTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8916DBF29211BE500124085 /* ContentSizedTableView.swift */; };
D8A6AB6C291C5136003AB663 /* MastodonLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */; };
D8A6FE5B293244B500666A47 /* WelcomeContentPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */; };
D8A6FE5D293244C300666A47 /* WelcomeContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */; };
D8A6FE5F29324BBC00666A47 /* WelcomeContentPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */; };
DB0009A626AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; settings = {ATTRIBUTES = (codegen, ); }; };
DB0009A726AEE5DC009B9D2D /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DB0009A926AEE5DC009B9D2D /* Intents.intentdefinition */; };
DB0140CF25C42AEE00F9F3CF /* OSLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0140CE25C42AEE00F9F3CF /* OSLog.swift */; };
@ -660,6 +663,9 @@
D87BFC8E291EC26A00FEE264 /* MastodonLoginServerTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginServerTableViewCell.swift; sourceTree = "<group>"; };
D8916DBF29211BE500124085 /* ContentSizedTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentSizedTableView.swift; sourceTree = "<group>"; };
D8A6AB6B291C5136003AB663 /* MastodonLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonLoginViewController.swift; sourceTree = "<group>"; };
D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentPage.swift; sourceTree = "<group>"; };
D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentViewController.swift; sourceTree = "<group>"; };
D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeContentPageView.swift; sourceTree = "<group>"; };
DB0009A826AEE5DC009B9D2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
DB0009AD26AEE5E4009B9D2D /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Intents.strings; sourceTree = "<group>"; };
DB0140CE25C42AEE00F9F3CF /* OSLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OSLog.swift; sourceTree = "<group>"; };
@ -1575,6 +1581,16 @@
path = Login;
sourceTree = "<group>";
};
D8A6FE5929323DC200666A47 /* Pages */ = {
isa = PBXGroup;
children = (
D8A6FE5A293244B500666A47 /* WelcomeContentPage.swift */,
D8A6FE5C293244C300666A47 /* WelcomeContentViewController.swift */,
D8A6FE5E29324BBC00666A47 /* WelcomeContentPageView.swift */,
);
path = Pages;
sourceTree = "<group>";
};
DB01409B25C40BB600F9F3CF /* Onboarding */ = {
isa = PBXGroup;
children = (
@ -2498,6 +2514,7 @@
DBABE3F125ECAC4E00879EE5 /* View */ = {
isa = PBXGroup;
children = (
D8A6FE5929323DC200666A47 /* Pages */,
DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */,
DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */,
DB0617EA277EF3820030EE79 /* GradientBorderView.swift */,
@ -3287,6 +3304,8 @@
DB603113279EBEBA00A935FE /* DataSourceFacade+Block.swift in Sources */,
DB63F777279A9A2A00455B82 /* NotificationView+Configuration.swift in Sources */,
DB029E95266A20430062874E /* MastodonAuthenticationController.swift in Sources */,
DB0C947726A7FE840088FB11 /* NotificationAvatarButton.swift in Sources */,
D8A6FE5F29324BBC00666A47 /* WelcomeContentPageView.swift in Sources */,
5B90C461262599800002E742 /* SettingsLinkTableViewCell.swift in Sources */,
DB6180DD263918E30018D199 /* MediaPreviewViewController.swift in Sources */,
DBE3CDEC261C6B2900430CC6 /* FavoriteViewController.swift in Sources */,
@ -3499,6 +3518,7 @@
DB1D84382657B275000346B3 /* SegmentedControlNavigateable.swift in Sources */,
0F20220726134DA4000C64BF /* HashtagTimelineViewModel+Diffable.swift in Sources */,
DB7A9F932818F33C0016AF98 /* MastodonServerRulesViewController+Debug.swift in Sources */,
D8A6FE5D293244C300666A47 /* WelcomeContentViewController.swift in Sources */,
2D5A3D2825CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift in Sources */,
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
@ -3574,6 +3594,7 @@
2D6DE40026141DF600A63F6A /* SearchViewModel.swift in Sources */,
DB0617FD27855BFE0030EE79 /* ServerRuleItem.swift in Sources */,
5BB04FD5262E7AFF0043BFF6 /* ReportViewController.swift in Sources */,
D8A6FE5B293244B500666A47 /* WelcomeContentPage.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -1,257 +0,0 @@
{
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "8dd85aee02e39dd280c75eef88ffdb86eed4b07b",
"version" : "5.6.2"
}
},
{
"identity" : "alamofireimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/AlamofireImage.git",
"state" : {
"revision" : "98cbb00ce0ec5fc8e52a5b50a6bfc08d3e5aee10",
"version" : "4.2.0"
}
},
{
"identity" : "commonoslog",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/CommonOSLog",
"state" : {
"revision" : "c121624a30698e9886efe38aebb36ff51c01b6c2",
"version" : "0.1.1"
}
},
{
"identity" : "faviconfinder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/will-lumley/FaviconFinder.git",
"state" : {
"revision" : "1f74844f77f79b95c0bb0130b3a87d4f340e6d3a",
"version" : "3.3.0"
}
},
{
"identity" : "flanimatedimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Flipboard/FLAnimatedImage.git",
"state" : {
"revision" : "d4f07b6f164d53c1212c3e54d6460738b1981e9f",
"version" : "1.0.17"
}
},
{
"identity" : "fpsindicator",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/FPSIndicator.git",
"state" : {
"revision" : "e4a5067ccd5293b024c767f09e51056afd4a4796",
"version" : "1.1.0"
}
},
{
"identity" : "fuzi",
"kind" : "remoteSourceControl",
"location" : "https://github.com/cezheng/Fuzi.git",
"state" : {
"revision" : "f08c8323da21e985f3772610753bcfc652c2103f",
"version" : "3.1.3"
}
},
{
"identity" : "keychainaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess.git",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
}
},
{
"identity" : "kingfisher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/onevcat/Kingfisher.git",
"state" : {
"revision" : "44e891bdb61426a95e31492a67c7c0dfad1f87c5",
"version" : "7.4.1"
}
},
{
"identity" : "metatextkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TwidereProject/MetaTextKit.git",
"state" : {
"revision" : "dcd5255d6930c2fab408dc8562c577547e477624",
"version" : "2.2.5"
}
},
{
"identity" : "nextlevelsessionexporter",
"kind" : "remoteSourceControl",
"location" : "https://github.com/NextLevel/NextLevelSessionExporter.git",
"state" : {
"revision" : "b6c0cce1aa37fe1547d694f958fac3c3524b74da",
"version" : "0.4.6"
}
},
{
"identity" : "nuke",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kean/Nuke.git",
"state" : {
"revision" : "a002b7fd786f2df2ed4333fe73a9727499fd9d97",
"version" : "10.11.2"
}
},
{
"identity" : "nuke-flanimatedimage-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kean/Nuke-FLAnimatedImage-Plugin.git",
"state" : {
"revision" : "b59c346a7d536336db3b0f12c72c6e53ee709e16",
"version" : "8.0.0"
}
},
{
"identity" : "pageboy",
"kind" : "remoteSourceControl",
"location" : "https://github.com/uias/Pageboy",
"state" : {
"revision" : "af8fa81788b893205e1ff42ddd88c5b0b315d7c5",
"version" : "3.7.0"
}
},
{
"identity" : "panmodal",
"kind" : "remoteSourceControl",
"location" : "https://github.com/slackhq/PanModal.git",
"state" : {
"revision" : "b012aecb6b67a8e46369227f893c12544846613f",
"version" : "1.2.7"
}
},
{
"identity" : "sdwebimage",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SDWebImage/SDWebImage.git",
"state" : {
"revision" : "3312bf5e67b52fbce7c3caf431b0cda721a9f7bb",
"version" : "5.14.2"
}
},
{
"identity" : "stripes",
"kind" : "remoteSourceControl",
"location" : "https://github.com/eneko/Stripes.git",
"state" : {
"revision" : "d533fd44b8043a3abbf523e733599173d6f98c11",
"version" : "0.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "f504716c27d2e5d4144fa4794b12129301d17729",
"version" : "1.0.3"
}
},
{
"identity" : "swift-nio",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio.git",
"state" : {
"revision" : "546610d52b19be3e19935e0880bb06b9c03f5cef",
"version" : "1.14.4"
}
},
{
"identity" : "swift-nio-zlib-support",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-nio-zlib-support.git",
"state" : {
"revision" : "37760e9a52030bb9011972c5213c3350fa9d41fd",
"version" : "1.0.0"
}
},
{
"identity" : "swiftsoup",
"kind" : "remoteSourceControl",
"location" : "https://github.com/scinfu/SwiftSoup.git",
"state" : {
"revision" : "6778575285177365cbad3e5b8a72f2a20583cfec",
"version" : "2.4.3"
}
},
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
"version" : "0.1.4"
}
},
{
"identity" : "tabbarpager",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TwidereProject/TabBarPager.git",
"state" : {
"revision" : "488aa66d157a648901b61721212c0dec23d27ee5",
"version" : "0.1.0"
}
},
{
"identity" : "tabman",
"kind" : "remoteSourceControl",
"location" : "https://github.com/uias/Tabman",
"state" : {
"revision" : "4a4f7c755b875ffd4f9ef10d67a67883669d2465",
"version" : "2.13.0"
}
},
{
"identity" : "thirdpartymailer",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vtourraine/ThirdPartyMailer.git",
"state" : {
"revision" : "44c1cfaa6969963f22691aa67f88a69e3b6d651f",
"version" : "2.1.0"
}
},
{
"identity" : "tocropviewcontroller",
"kind" : "remoteSourceControl",
"location" : "https://github.com/TimOliver/TOCropViewController.git",
"state" : {
"revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e",
"version" : "2.6.1"
}
},
{
"identity" : "uihostingconfigurationbackport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/woxtu/UIHostingConfigurationBackport.git",
"state" : {
"revision" : "6091f2d38faa4b24fc2ca0389c651e2f666624a3",
"version" : "0.1.0"
}
},
{
"identity" : "uitextview-placeholder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/MainasuK/UITextView-Placeholder.git",
"state" : {
"revision" : "20f513ded04a040cdf5467f0891849b1763ede3b",
"version" : "1.4.1"
}
}
],
"version" : 2
}

View File

@ -0,0 +1,48 @@
//
// WelcomeContentPage.swift
// Mastodon
//
// Created by Nathan Mattes on 26.11.22.
//
import UIKit
enum WelcomeContentPage: CaseIterable {
case whatIsMastodon
case mastodonIsLikeThat
case howDoIPickAServer
var backgroundColor: UIColor {
switch self {
case .whatIsMastodon:
return .green
case .mastodonIsLikeThat:
return .red
case .howDoIPickAServer:
return .blue
}
}
var title: String {
switch self {
case .whatIsMastodon:
return "What is Mastodon?"
case .mastodonIsLikeThat:
return "Mastodon is like that"
case .howDoIPickAServer:
return "How to I pick a server?"
}
}
var content: String {
switch self {
case .whatIsMastodon:
return "Long text\n\nhat is Mastodon?"
case .mastodonIsLikeThat:
return "Long text\n\nwhat Mastodon is like"
case .howDoIPickAServer:
return "Long text\n\nHow to I pick a server?"
}
}
}

View File

@ -0,0 +1,53 @@
//
// WelcomeContentPageView.swift
// Mastodon
//
// Created by Nathan Mattes on 26.11.22.
//
import UIKit
class WelcomeContentPageView: UIView {
//TODO: Put in ScrollView?
private let contentStackView: UIStackView
private let titleView: UILabel
private let label: UILabel
init(page: WelcomeContentPage) {
//TODO: @zeitschlag Decide based on page which titleView, first page has mastodon-logo in it
//TODO: @zeitschlag Add styling
titleView = UILabel()
titleView.text = page.title
//TODO: @zeitschlag Add styling
label = UILabel()
label.text = page.content
label.numberOfLines = 0
contentStackView = UIStackView(arrangedSubviews: [titleView, label, UIView()])
contentStackView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.axis = .vertical
contentStackView.alignment = .leading
super.init(frame: .zero)
addSubview(contentStackView)
setupConstraints()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
private func setupConstraints() {
let constraints = [
contentStackView.topAnchor.constraint(equalTo: topAnchor),
contentStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
trailingAnchor.constraint(equalTo: contentStackView.trailingAnchor, constant: 16),
bottomAnchor.constraint(equalTo: contentStackView.bottomAnchor)
]
NSLayoutConstraint.activate(constraints)
}
}

View File

@ -0,0 +1,28 @@
//
// WelcomeContentViewController.swift
// Mastodon
//
// Created by Nathan Mattes on 26.11.22.
//
import UIKit
class WelcomeContentViewController: UIViewController {
let page: WelcomeContentPage
var contentView: WelcomeContentPageView {
view as! WelcomeContentPageView
}
init(page: WelcomeContentPage) {
self.page = page
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
let pageView = WelcomeContentPageView(page: page)
self.view = pageView
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}

View File

@ -5,7 +5,6 @@
// Created by BradGao on 2021/2/20.
//
import os.log
import UIKit
import Combine
import MastodonAsset
@ -13,318 +12,334 @@ import MastodonCore
import MastodonLocalization
final class WelcomeViewController: UIViewController, NeedsDependency {
let logger = Logger(subsystem: "WelcomeViewController", category: "ViewController")
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>()
var observations = Set<NSKeyValueObservation>()
private(set) lazy var viewModel = WelcomeViewModel(context: context)
let welcomeIllustrationView = WelcomeIllustrationView()
var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint?
private(set) lazy var dismissBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(WelcomeViewController.dismissBarButtonItemDidPressed(_:)))
private(set) lazy var logoImageView: UIImageView = {
let image = Asset.Scene.Welcome.mastodonLogo.image
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private(set) lazy var sloganLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold))
label.textColor = Asset.Colors.Label.primary.color
label.text = L10n.Scene.Welcome.slogan
label.adjustsFontForContentSizeCategory = true
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
let buttonContainer = UIStackView()
private(set) lazy var signUpButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
let backgroundImageColor: UIColor = .white
let backgroundImageHighlightedColor: UIColor = UIColor(white: 0.8, alpha: 1.0)
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
button.setTitleColor(.black, for: .normal)
return button
}()
let signUpButtonShadowView = UIView()
private(set) lazy var signInButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Scene.Welcome.logIn, for: .normal)
let backgroundImageColor = Asset.Scene.Welcome.signInButtonBackground.color
let backgroundImageHighlightedColor = Asset.Scene.Welcome.signInButtonBackground.color.withAlphaComponent(0.8)
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
let titleColor: UIColor = UIColor.white.withAlphaComponent(0.9)
button.setTitleColor(titleColor, for: .normal)
return button
}()
let signInButtonShadowView = UIView()
deinit {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
}
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var disposeBag = Set<AnyCancellable>()
var observations = Set<NSKeyValueObservation>()
private(set) lazy var viewModel = WelcomeViewModel(context: context)
let welcomeIllustrationView = WelcomeIllustrationView()
var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint?
private(set) lazy var dismissBarButtonItem = UIBarButtonItem(barButtonSystemItem: .close, target: self, action: #selector(WelcomeViewController.dismissBarButtonItemDidPressed(_:)))
private(set) lazy var logoImageView: UIImageView = {
let image = Asset.Scene.Welcome.mastodonLogo.image
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.isHidden = true
return imageView
}()
private(set) lazy var sloganLabel: UILabel = {
let label = UILabel()
label.font = UIFontMetrics(forTextStyle: .largeTitle).scaledFont(for: .systemFont(ofSize: 34, weight: .bold))
label.textColor = Asset.Colors.Label.primary.color
label.text = L10n.Scene.Welcome.slogan
label.adjustsFontForContentSizeCategory = true
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
let buttonContainer = UIStackView()
private(set) lazy var signUpButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
let backgroundImageColor: UIColor = .white
let backgroundImageHighlightedColor: UIColor = UIColor(white: 0.8, alpha: 1.0)
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
button.setTitleColor(.black, for: .normal)
return button
}()
let signUpButtonShadowView = UIView()
private(set) lazy var signInButton: PrimaryActionButton = {
let button = PrimaryActionButton()
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
button.contentEdgeInsets = WelcomeViewController.actionButtonPadding
button.titleLabel?.adjustsFontForContentSizeCategory = true
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold))
button.setTitle(L10n.Scene.Welcome.logIn, for: .normal)
let backgroundImageColor = Asset.Scene.Welcome.signInButtonBackground.color
let backgroundImageHighlightedColor = Asset.Scene.Welcome.signInButtonBackground.color.withAlphaComponent(0.8)
button.setBackgroundImage(.placeholder(color: backgroundImageColor), for: .normal)
button.setBackgroundImage(.placeholder(color: backgroundImageHighlightedColor), for: .highlighted)
let titleColor: UIColor = UIColor.white.withAlphaComponent(0.9)
button.setTitleColor(titleColor, for: .normal)
return button
}()
let signInButtonShadowView = UIView()
private(set) lazy var pageViewController: UIPageViewController = {
let pageController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
pageController.setViewControllers([WelcomeContentViewController(page: .whatIsMastodon)], direction: .forward, animated: false)
return pageController
}()
var currentPage: WelcomeContentPage = .whatIsMastodon
}
extension WelcomeViewController {
override func viewDidLoad() {
super.viewDidLoad()
definesPresentationContext = true
preferredContentSize = CGSize(width: 547, height: 678)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .never
view.overrideUserInterfaceStyle = .light
setupOnboardingAppearance()
setupIllustrationLayout()
buttonContainer.axis = .vertical
buttonContainer.spacing = 12
buttonContainer.isLayoutMarginsRelativeArrangement = true
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(buttonContainer)
NSLayoutConstraint.activate([
buttonContainer.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
buttonContainer.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor),
])
signUpButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signUpButton)
NSLayoutConstraint.activate([
signUpButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signInButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signInButton)
NSLayoutConstraint.activate([
signInButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signUpButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signUpButtonShadowView)
buttonContainer.sendSubviewToBack(signUpButtonShadowView)
signUpButtonShadowView.pinTo(to: signUpButton)
signInButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signInButtonShadowView)
buttonContainer.sendSubviewToBack(signInButtonShadowView)
signInButtonShadowView.pinTo(to: signInButton)
signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside)
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
viewModel.$needsShowDismissEntry
.receive(on: DispatchQueue.main)
.sink { [weak self] needsShowDismissEntry in
guard let self = self else { return }
self.navigationItem.leftBarButtonItem = needsShowDismissEntry ? self.dismissBarButtonItem : nil
}
.store(in: &disposeBag)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupButtonShadowView()
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
var overlap: CGFloat = 5
// shift illustration down for non-notch phone
if view.safeAreaInsets.bottom == 0 {
overlap += 56
}
welcomeIllustrationViewBottomAnchorLayoutConstraint?.constant = overlap
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
override func viewDidLoad() {
super.viewDidLoad()
view.layoutIfNeeded()
definesPresentationContext = true
preferredContentSize = CGSize(width: 547, height: 678)
setupIllustrationLayout()
setupButtonShadowView()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .never
view.overrideUserInterfaceStyle = .light
setupOnboardingAppearance()
setupIllustrationLayout()
buttonContainer.axis = .vertical
buttonContainer.spacing = 12
buttonContainer.isLayoutMarginsRelativeArrangement = true
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(buttonContainer)
NSLayoutConstraint.activate([
buttonContainer.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor),
buttonContainer.trailingAnchor.constraint(equalTo: view.readableContentGuide.trailingAnchor),
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: buttonContainer.bottomAnchor),
])
signUpButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signUpButton)
NSLayoutConstraint.activate([
signUpButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signInButton.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addArrangedSubview(signInButton)
NSLayoutConstraint.activate([
signInButton.heightAnchor.constraint(greaterThanOrEqualToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
])
signUpButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signUpButtonShadowView)
buttonContainer.sendSubviewToBack(signUpButtonShadowView)
signUpButtonShadowView.pinTo(to: signUpButton)
signInButtonShadowView.translatesAutoresizingMaskIntoConstraints = false
buttonContainer.addSubview(signInButtonShadowView)
buttonContainer.sendSubviewToBack(signInButtonShadowView)
signInButtonShadowView.pinTo(to: signInButton)
signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside)
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
pageViewController.delegate = self
pageViewController.dataSource = self
addChild(pageViewController)
pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pageViewController.view)
pageViewController.didMove(toParent: self)
NSLayoutConstraint.activate([
pageViewController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pageViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
view.trailingAnchor.constraint(equalTo: pageViewController.view.trailingAnchor),
buttonContainer.topAnchor.constraint(equalTo: pageViewController.view.bottomAnchor, constant: 16),
])
viewModel.$needsShowDismissEntry
.receive(on: DispatchQueue.main)
.sink { [weak self] needsShowDismissEntry in
guard let self = self else { return }
self.navigationItem.leftBarButtonItem = needsShowDismissEntry ? self.dismissBarButtonItem : nil
}
.store(in: &disposeBag)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupButtonShadowView()
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
var overlap: CGFloat = 5
// shift illustration down for non-notch phone
if view.safeAreaInsets.bottom == 0 {
overlap += 56
}
welcomeIllustrationViewBottomAnchorLayoutConstraint?.constant = overlap
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
view.layoutIfNeeded()
setupIllustrationLayout()
setupButtonShadowView()
}
}
extension WelcomeViewController {
private func setupButtonShadowView() {
signUpButtonShadowView.layer.setupShadow(
color: .black,
alpha: 0.25,
x: 0,
y: 1,
blur: 2,
spread: 0,
roundedRect: signUpButtonShadowView.bounds,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: 10, height: 10)
private func setupButtonShadowView() {
signUpButtonShadowView.layer.setupShadow(
color: .black,
alpha: 0.25,
x: 0,
y: 1,
blur: 2,
spread: 0,
roundedRect: signUpButtonShadowView.bounds,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: 10, height: 10)
)
signInButtonShadowView.layer.setupShadow(
color: .black,
alpha: 0.25,
x: 0,
y: 1,
blur: 2,
spread: 0,
roundedRect: signInButtonShadowView.bounds,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: 10, height: 10)
)
}
private func updateButtonContainerLayoutMargins(traitCollection: UITraitCollection) {
switch traitCollection.userInterfaceIdiom {
case .phone:
buttonContainer.layoutMargins = UIEdgeInsets(
top: 0,
left: WelcomeViewController.actionButtonMargin,
bottom: WelcomeViewController.viewBottomPaddingHeight,
right: WelcomeViewController.actionButtonMargin
)
signInButtonShadowView.layer.setupShadow(
color: .black,
alpha: 0.25,
x: 0,
y: 1,
blur: 2,
spread: 0,
roundedRect: signInButtonShadowView.bounds,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: 10, height: 10)
default:
let margin = traitCollection.horizontalSizeClass == .regular ? WelcomeViewController.actionButtonMarginExtend : WelcomeViewController.actionButtonMargin
buttonContainer.layoutMargins = UIEdgeInsets(
top: 0,
left: margin,
bottom: WelcomeViewController.viewBottomPaddingHeightExtend,
right: margin
)
}
private func updateButtonContainerLayoutMargins(traitCollection: UITraitCollection) {
switch traitCollection.userInterfaceIdiom {
}
private func setupIllustrationLayout() {
welcomeIllustrationView.layout = {
switch traitCollection.userInterfaceIdiom {
case .phone:
buttonContainer.layoutMargins = UIEdgeInsets(
top: 0,
left: WelcomeViewController.actionButtonMargin,
bottom: WelcomeViewController.viewBottomPaddingHeight,
right: WelcomeViewController.actionButtonMargin
)
return .compact
default:
let margin = traitCollection.horizontalSizeClass == .regular ? WelcomeViewController.actionButtonMarginExtend : WelcomeViewController.actionButtonMargin
buttonContainer.layoutMargins = UIEdgeInsets(
top: 0,
left: margin,
bottom: WelcomeViewController.viewBottomPaddingHeightExtend,
right: margin
)
}
return .regular
}
}()
// set logo
if logoImageView.superview == nil {
view.addSubview(logoImageView)
NSLayoutConstraint.activate([
logoImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
logoImageView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 35),
view.readableContentGuide.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor, constant: 35),
logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor, multiplier: 75.0/269.0),
])
logoImageView.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
private func setupIllustrationLayout() {
welcomeIllustrationView.layout = {
switch traitCollection.userInterfaceIdiom {
case .phone:
return .compact
default:
return .regular
}
}()
// set logo
if logoImageView.superview == nil {
view.addSubview(logoImageView)
NSLayoutConstraint.activate([
logoImageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
logoImageView.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: 35),
view.readableContentGuide.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor, constant: 35),
logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor, multiplier: 75.0/269.0),
])
logoImageView.setContentHuggingPriority(.defaultHigh, for: .vertical)
}
// set illustration
guard welcomeIllustrationView.superview == nil else {
return
}
welcomeIllustrationView.contentMode = .scaleAspectFit
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5)
view.addSubview(welcomeIllustrationView)
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.leftAnchor, constant: 15),
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 15),
welcomeIllustrationViewBottomAnchorLayoutConstraint!.priority(.required - 1),
])
welcomeIllustrationView.cloudBaseImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -5, maxX: 5, minY: -5, maxY: 5)
)
welcomeIllustrationView.rightHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -15, maxX: 25, minY: -10, maxY: 10)
)
welcomeIllustrationView.leftHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -25, maxX: 15, minY: -15, maxY: 15)
)
welcomeIllustrationView.centerHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -14, maxX: 14, minY: -5, maxY: 25)
)
let topPaddingView = UIView()
topPaddingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topPaddingView)
NSLayoutConstraint.activate([
topPaddingView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor),
topPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
topPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
])
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(welcomeIllustrationView.elephantOnAirplaneWithContrailImageView)
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.leftAnchor, constant: 12), // add 12pt bleeding
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.topAnchor.constraint(equalTo: topPaddingView.bottomAnchor),
// make a little bit large
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.84),
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.heightAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor, multiplier: 105.0/318.0),
])
let bottomPaddingView = UIView()
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bottomPaddingView)
NSLayoutConstraint.activate([
bottomPaddingView.topAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.bottomAnchor),
bottomPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
bottomPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
bottomPaddingView.bottomAnchor.constraint(equalTo: view.centerYAnchor),
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 4),
])
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -20, maxX: 12, minY: -20, maxY: 12) // maxX should not larger then the bleeding (12pt)
)
view.bringSubviewToFront(logoImageView)
view.bringSubviewToFront(sloganLabel)
// set illustration
guard welcomeIllustrationView.superview == nil else {
return
}
welcomeIllustrationView.contentMode = .scaleAspectFit
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5)
view.addSubview(welcomeIllustrationView)
// welcomeIllustrationView.isHidden = true
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.leftAnchor, constant: 15),
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 15),
welcomeIllustrationViewBottomAnchorLayoutConstraint!.priority(.required - 1),
])
welcomeIllustrationView.cloudBaseImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -5, maxX: 5, minY: -5, maxY: 5)
)
welcomeIllustrationView.rightHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -15, maxX: 25, minY: -10, maxY: 10)
)
welcomeIllustrationView.leftHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -25, maxX: 15, minY: -15, maxY: 15)
)
welcomeIllustrationView.centerHillImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -14, maxX: 14, minY: -5, maxY: 25)
)
let topPaddingView = UIView()
topPaddingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topPaddingView)
NSLayoutConstraint.activate([
topPaddingView.topAnchor.constraint(equalTo: logoImageView.bottomAnchor),
topPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
topPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
])
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(welcomeIllustrationView.elephantOnAirplaneWithContrailImageView)
NSLayoutConstraint.activate([
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.leftAnchor, constant: 12), // add 12pt bleeding
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.topAnchor.constraint(equalTo: topPaddingView.bottomAnchor),
// make a little bit large
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.84),
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.heightAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.widthAnchor, multiplier: 105.0/318.0),
])
let bottomPaddingView = UIView()
bottomPaddingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bottomPaddingView)
NSLayoutConstraint.activate([
bottomPaddingView.topAnchor.constraint(equalTo: welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.bottomAnchor),
bottomPaddingView.leadingAnchor.constraint(equalTo: logoImageView.leadingAnchor),
bottomPaddingView.trailingAnchor.constraint(equalTo: logoImageView.trailingAnchor),
bottomPaddingView.bottomAnchor.constraint(equalTo: view.centerYAnchor),
bottomPaddingView.heightAnchor.constraint(equalTo: topPaddingView.heightAnchor, multiplier: 4),
])
welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.addMotionEffect(
UIInterpolatingMotionEffect.motionEffect(minX: -20, maxX: 12, minY: -20, maxY: 12) // maxX should not larger then the bleeding (12pt)
)
view.bringSubviewToFront(logoImageView)
view.bringSubviewToFront(sloganLabel)
}
}
extension WelcomeViewController {
@objc
private func signUpButtonDidClicked(_ sender: UIButton) {
_ = coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context)), from: self, transition: .show)
}
@objc
private func signInButtonDidClicked(_ sender: UIButton) {
_ = coordinator.present(scene: .mastodonLogin, from: self, transition: .show)
}
@objc
private func dismissBarButtonItemDidPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
@objc
private func signUpButtonDidClicked(_ sender: UIButton) {
_ = coordinator.present(scene: .mastodonPickServer(viewMode: MastodonPickServerViewModel(context: context)), from: self, transition: .show)
}
@objc
private func signInButtonDidClicked(_ sender: UIButton) {
_ = coordinator.present(scene: .mastodonLogin, from: self, transition: .show)
}
@objc
private func dismissBarButtonItemDidPressed(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
// MARK: - OnboardingViewControllerAppearance
@ -343,36 +358,88 @@ extension WelcomeViewController: OnboardingViewControllerAppearance {
// MARK: - UIAdaptivePresentationControllerDelegate
extension WelcomeViewController: UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
// update button layout
updateButtonContainerLayoutMargins(traitCollection: traitCollection)
let navigationController = navigationController as? OnboardingNavigationController
switch traitCollection.userInterfaceIdiom {
case .phone:
// update button layout
updateButtonContainerLayoutMargins(traitCollection: traitCollection)
let navigationController = navigationController as? OnboardingNavigationController
switch traitCollection.userInterfaceIdiom {
case .phone:
navigationController?.gradientBorderView.isHidden = true
// make underneath view controller alive to fix layout issue due to view life cycle
return .fullScreen
default:
switch traitCollection.horizontalSizeClass {
case .compact:
navigationController?.gradientBorderView.isHidden = true
// make underneath view controller alive to fix layout issue due to view life cycle
return .fullScreen
default:
switch traitCollection.horizontalSizeClass {
case .compact:
navigationController?.gradientBorderView.isHidden = true
return .fullScreen
default:
navigationController?.gradientBorderView.isHidden = false
return .formSheet
}
default:
navigationController?.gradientBorderView.isHidden = false
return .formSheet
}
}
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
}
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return nil
}
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
return false
}
}
//MARK: - UIPageViewControllerDelegate
extension WelcomeViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard let currentViewController = pageViewController.viewControllers?.first as? WelcomeContentViewController else { return }
currentPage = currentViewController.page
}
}
//MARK: - UIPageViewDataSource
extension WelcomeViewController: UIPageViewControllerDataSource {
func presentationIndex(for pageViewController: UIPageViewController) -> Int {
WelcomeContentPage.allCases.firstIndex(of: currentPage) ?? 0
}
func presentationCount(for pageViewController: UIPageViewController) -> Int {
return WelcomeContentPage.allCases.count
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let viewController = viewController as? WelcomeContentViewController else { return nil }
let currentPage = viewController.page
switch currentPage {
case .whatIsMastodon:
return nil
case .mastodonIsLikeThat:
return WelcomeContentViewController(page: .whatIsMastodon)
case .howDoIPickAServer:
return WelcomeContentViewController(page: .mastodonIsLikeThat)
}
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let viewController = viewController as? WelcomeContentViewController else { return nil }
let currentPage = viewController.page
switch currentPage {
case .whatIsMastodon:
return WelcomeContentViewController(page: .mastodonIsLikeThat)
case .mastodonIsLikeThat:
return WelcomeContentViewController(page: .howDoIPickAServer)
case .howDoIPickAServer:
return nil
}
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
return false
}
}
}