forked from zelo72/mastodon-ios
feat: update Welcome scene UI
This commit is contained in:
parent
7711564cdd
commit
4a38daa345
|
@ -193,7 +193,9 @@
|
|||
},
|
||||
"scene": {
|
||||
"welcome": {
|
||||
"slogan": "Social networking\nback in your hands."
|
||||
"slogan": "Social networking\nback in your hands.",
|
||||
"get_started": "Get Started",
|
||||
"log_in": "Log In"
|
||||
},
|
||||
"server_picker": {
|
||||
"title": "Pick a server,\nany server.",
|
||||
|
@ -554,4 +556,4 @@
|
|||
"accessibility_hint": "Double tap to dismiss this wizard"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,6 +191,7 @@
|
|||
DB03F7F32689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */; };
|
||||
DB03F7F52689B782007B274C /* ComposeTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB03F7F42689B782007B274C /* ComposeTableView.swift */; };
|
||||
DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB040ED026538E3C00BEE9D8 /* Trie.swift */; };
|
||||
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0617EA277EF3820030EE79 /* GradientBorderView.swift */; };
|
||||
DB084B5725CBC56C00F898ED /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB084B5625CBC56C00F898ED /* Status.swift */; };
|
||||
DB0AC6FC25CD02E600D75117 /* APIService+Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */; };
|
||||
DB0C946526A6FD4D0088FB11 /* AlamofireImage in Frameworks */ = {isa = PBXBuildFile; productRef = DB0C946426A6FD4D0088FB11 /* AlamofireImage */; };
|
||||
|
@ -969,6 +970,7 @@
|
|||
DB03F7F22689AEA3007B274C /* ComposeRepliedToStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToStatusContentTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DB03F7F42689B782007B274C /* ComposeTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTableView.swift; sourceTree = "<group>"; };
|
||||
DB040ED026538E3C00BEE9D8 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = "<group>"; };
|
||||
DB0617EA277EF3820030EE79 /* GradientBorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientBorderView.swift; sourceTree = "<group>"; };
|
||||
DB084B5625CBC56C00F898ED /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
||||
DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Instance.swift"; sourceTree = "<group>"; };
|
||||
DB0C946A26A700AB0088FB11 /* MastodonUser+Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonUser+Property.swift"; sourceTree = "<group>"; };
|
||||
|
@ -2993,6 +2995,7 @@
|
|||
children = (
|
||||
DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */,
|
||||
DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */,
|
||||
DB0617EA277EF3820030EE79 /* GradientBorderView.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4307,6 +4310,7 @@
|
|||
DB4932B326F2054200EF46D4 /* CircleAvatarButton.swift in Sources */,
|
||||
0FB3D30825E524C600AAD544 /* PickServerCategoriesCell.swift in Sources */,
|
||||
2D4AD8A226316CD200613EFC /* SelectedAccountSection.swift in Sources */,
|
||||
DB0617EB277EF3820030EE79 /* GradientBorderView.swift in Sources */,
|
||||
DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */,
|
||||
DBB525362611ECEB002F1F29 /* UserTimelineViewController.swift in Sources */,
|
||||
DB6D1B3D2636857500ACB481 /* AppearancePreference.swift in Sources */,
|
||||
|
@ -4838,7 +4842,7 @@
|
|||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -4866,7 +4870,7 @@
|
|||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Mastodon/Vender/Mastodon-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
@ -5131,7 +5135,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -5156,7 +5160,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
@ -5181,7 +5185,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -5206,7 +5210,7 @@
|
|||
SKIP_INSTALL = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "APP_EXTENSION $(inherited)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
@ -5230,7 +5234,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -5254,7 +5258,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
<key>AppShared.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>24</integer>
|
||||
<integer>18</integer>
|
||||
</dict>
|
||||
<key>CoreDataStack.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>26</integer>
|
||||
<integer>19</integer>
|
||||
</dict>
|
||||
<key>Mastodon - ASDK.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -102,7 +102,7 @@
|
|||
<key>MastodonIntent.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>27</integer>
|
||||
<integer>21</integer>
|
||||
</dict>
|
||||
<key>MastodonIntents.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
|
@ -122,7 +122,7 @@
|
|||
<key>ShareActionExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>25</integer>
|
||||
<integer>20</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
@ -255,7 +255,7 @@ extension SceneCoordinator {
|
|||
DispatchQueue.main.async {
|
||||
self.present(
|
||||
scene: .welcome,
|
||||
from: nil,
|
||||
from: self.sceneDelegate.window?.rootViewController,
|
||||
transition: .modal(animated: animated, completion: nil)
|
||||
)
|
||||
}
|
||||
|
@ -304,14 +304,20 @@ extension SceneCoordinator {
|
|||
presentingViewController.showDetailViewController(navigationController, sender: sender)
|
||||
|
||||
case .modal(let animated, let completion):
|
||||
let modalNavigationController: UINavigationController = {
|
||||
if scene.isOnboarding {
|
||||
return AdaptiveStatusBarStyleNavigationController(rootViewController: viewController)
|
||||
} else {
|
||||
return UINavigationController(rootViewController: viewController)
|
||||
}
|
||||
}()
|
||||
modalNavigationController.modalPresentationCapturesStatusBarAppearance = true
|
||||
// let modalNavigationController: UINavigationController = {
|
||||
// if scene.isOnboarding {
|
||||
// return AdaptiveStatusBarStyleNavigationController(rootViewController: viewController)
|
||||
// } else {
|
||||
// return UINavigationController(rootViewController: viewController)
|
||||
// }
|
||||
// }()
|
||||
// modalNavigationController.modalPresentationCapturesStatusBarAppearance = true
|
||||
// if let adaptivePresentationControllerDelegate = viewController as? UIAdaptivePresentationControllerDelegate {
|
||||
// modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate
|
||||
// }
|
||||
// presentingViewController.present(modalNavigationController, animated: animated, completion: completion)
|
||||
|
||||
let modalNavigationController = UINavigationController(rootViewController: viewController)
|
||||
if let adaptivePresentationControllerDelegate = viewController as? UIAdaptivePresentationControllerDelegate {
|
||||
modalNavigationController.presentationController?.delegate = adaptivePresentationControllerDelegate
|
||||
}
|
||||
|
|
|
@ -102,8 +102,10 @@ internal enum Asset {
|
|||
internal enum Welcome {
|
||||
internal enum Illustration {
|
||||
internal static let backgroundCyan = ColorAsset(name: "Scene/Welcome/illustration/background.cyan")
|
||||
internal static let cloudBaseExtend = ImageAsset(name: "Scene/Welcome/illustration/cloud.base.extend")
|
||||
internal static let cloudBase = ImageAsset(name: "Scene/Welcome/illustration/cloud.base")
|
||||
internal static let elephantOnAirplaneWithContrail = ImageAsset(name: "Scene/Welcome/illustration/elephant.on.airplane.with.contrail")
|
||||
internal static let elephantThreeOnGrassExtend = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass.extend")
|
||||
internal static let elephantThreeOnGrass = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass")
|
||||
internal static let elephantThreeOnGrassWithTreeThree = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass.with.tree.three")
|
||||
internal static let elephantThreeOnGrassWithTreeTwo = ImageAsset(name: "Scene/Welcome/illustration/elephant.three.on.grass.with.tree.two")
|
||||
|
@ -112,6 +114,7 @@ internal enum Asset {
|
|||
internal static let mastodonLogoBlackLarge = ImageAsset(name: "Scene/Welcome/mastodon.logo.black.large")
|
||||
internal static let mastodonLogo = ImageAsset(name: "Scene/Welcome/mastodon.logo")
|
||||
internal static let mastodonLogoLarge = ImageAsset(name: "Scene/Welcome/mastodon.logo.large")
|
||||
internal static let signInButtonBackground = ColorAsset(name: "Scene/Welcome/sign.in.button.background")
|
||||
}
|
||||
}
|
||||
internal enum Settings {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "cloud.base.extend.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "cloud.base.extend@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "cloud.base.extend@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
Binary file not shown.
After Width: | Height: | Size: 73 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "elephant.three.on.grass.extend.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "elephant.three.on.grass.extend@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "elephant.three.on.grass.extend@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
After Width: | Height: | Size: 280 KiB |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x81",
|
||||
"green" : "0xAC",
|
||||
"red" : "0x58"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -15,9 +15,11 @@ protocol OnboardingViewControllerAppearance: UIViewController {
|
|||
|
||||
extension OnboardingViewControllerAppearance {
|
||||
|
||||
static var actionButtonHeight: CGFloat { return 46 }
|
||||
static var actionButtonHeight: CGFloat { return 50 }
|
||||
static var actionButtonMargin: CGFloat { return 12 }
|
||||
static var actionButtonMarginExtend: CGFloat { return 80 }
|
||||
static var viewBottomPaddingHeight: CGFloat { return 11 }
|
||||
static var viewBottomPaddingHeightExtend: CGFloat { return 22 }
|
||||
|
||||
func setupOnboardingAppearance() {
|
||||
view.backgroundColor = Asset.Theme.Mastodon.systemGroupedBackground.color
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
//
|
||||
// GradientBorderView.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK on 2021-12-31.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class GradientBorderView: UIView {
|
||||
|
||||
let gradientLayer = CAGradientLayer()
|
||||
let maskLayer = CAShapeLayer()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension GradientBorderView {
|
||||
private func _init() {
|
||||
isUserInteractionEnabled = false
|
||||
|
||||
gradientLayer.frame = bounds
|
||||
|
||||
gradientLayer.colors = [
|
||||
UIColor.white.cgColor,
|
||||
UIColor.white.withAlphaComponent(0.0).cgColor,
|
||||
]
|
||||
|
||||
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
|
||||
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
|
||||
|
||||
layer.addSublayer(gradientLayer)
|
||||
|
||||
// set blend mode to "Soft Light"
|
||||
layer.compositingFilter = "softLightBlendMode"
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
let bezierPath = UIBezierPath(rect: bounds)
|
||||
bezierPath.append(UIBezierPath(roundedRect: bounds.insetBy(dx: 3, dy: 3), cornerRadius: 10))
|
||||
|
||||
maskLayer.fillRule = .evenOdd
|
||||
maskLayer.path = bezierPath.cgPath
|
||||
|
||||
gradientLayer.frame = bounds
|
||||
gradientLayer.mask = maskLayer
|
||||
}
|
||||
}
|
|
@ -8,18 +8,18 @@
|
|||
import UIKit
|
||||
|
||||
final class WelcomeIllustrationView: UIView {
|
||||
|
||||
static let artworkImageSize = CGSize(width: 375, height: 1500)
|
||||
|
||||
|
||||
let cloudBaseImageView = UIImageView()
|
||||
let rightHillImageView = UIImageView()
|
||||
let leftHillImageView = UIImageView()
|
||||
let centerHillImageView = UIImageView()
|
||||
|
||||
private let cloudBaseImage = Asset.Scene.Welcome.Illustration.cloudBase.image
|
||||
private let cloudBaseExtendImage = Asset.Scene.Welcome.Illustration.cloudBaseExtend.image
|
||||
private let elephantThreeOnGrassWithTreeTwoImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrassWithTreeTwo.image
|
||||
private let elephantThreeOnGrassWithTreeThreeImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrassWithTreeThree.image
|
||||
private let elephantThreeOnGrassImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrass.image
|
||||
private let elephantThreeOnGrassExtendImage = Asset.Scene.Welcome.Illustration.elephantThreeOnGrassExtend.image
|
||||
|
||||
// layout outside
|
||||
let elephantOnAirplaneWithContrailImageView: UIImageView = {
|
||||
|
@ -27,6 +27,13 @@ final class WelcomeIllustrationView: UIView {
|
|||
imageView.contentMode = .scaleAspectFill
|
||||
return imageView
|
||||
}()
|
||||
|
||||
var layout: Layout = .compact {
|
||||
didSet {
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
var aspectLayoutConstraint: NSLayoutConstraint!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
@ -40,6 +47,20 @@ final class WelcomeIllustrationView: UIView {
|
|||
|
||||
}
|
||||
|
||||
extension WelcomeIllustrationView {
|
||||
enum Layout {
|
||||
case compact
|
||||
case regular
|
||||
|
||||
var artworkImageSize: CGSize {
|
||||
switch self {
|
||||
case .compact: return CGSize(width: 375, height: 1500)
|
||||
case .regular: return CGSize(width: 547, height: 1500)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension WelcomeIllustrationView {
|
||||
|
||||
private func _init() {
|
||||
|
@ -62,7 +83,6 @@ extension WelcomeIllustrationView {
|
|||
cloudBaseImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
cloudBaseImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
cloudBaseImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
cloudBaseImageView.widthAnchor.constraint(equalTo: cloudBaseImageView.heightAnchor, multiplier: WelcomeIllustrationView.artworkImageSize.width / WelcomeIllustrationView.artworkImageSize.height),
|
||||
])
|
||||
|
||||
[
|
||||
|
@ -79,15 +99,28 @@ extension WelcomeIllustrationView {
|
|||
imageView.bottomAnchor.constraint(equalTo: cloudBaseImageView.bottomAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
aspectLayoutConstraint = cloudBaseImageView.widthAnchor.constraint(equalTo: cloudBaseImageView.heightAnchor, multiplier: layout.artworkImageSize.width / layout.artworkImageSize.height)
|
||||
aspectLayoutConstraint.isActive = true
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
updateImage()
|
||||
|
||||
switch layout {
|
||||
case .compact:
|
||||
layoutCompact()
|
||||
case .regular:
|
||||
layoutRegular()
|
||||
}
|
||||
|
||||
aspectLayoutConstraint.isActive = false
|
||||
aspectLayoutConstraint = cloudBaseImageView.widthAnchor.constraint(equalTo: cloudBaseImageView.heightAnchor, multiplier: layout.artworkImageSize.width / layout.artworkImageSize.height)
|
||||
aspectLayoutConstraint.isActive = true
|
||||
}
|
||||
|
||||
private func updateImage() {
|
||||
let size = WelcomeIllustrationView.artworkImageSize
|
||||
private func layoutCompact() {
|
||||
let size = layout.artworkImageSize
|
||||
let width = size.width
|
||||
let height = size.height
|
||||
|
||||
|
@ -130,6 +163,50 @@ extension WelcomeIllustrationView {
|
|||
}
|
||||
}
|
||||
|
||||
private func layoutRegular() {
|
||||
let size = layout.artworkImageSize
|
||||
let width = size.width
|
||||
let height = size.height
|
||||
|
||||
cloudBaseImageView.image = UIGraphicsImageRenderer(size: size).image { context in
|
||||
// clear background
|
||||
UIColor.clear.setFill()
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
// draw cloud
|
||||
cloudBaseExtendImage.draw(at: CGPoint(x: 0, y: height - cloudBaseExtendImage.size.height))
|
||||
|
||||
rightHillImageView.image = UIGraphicsImageRenderer(size: size).image { context in
|
||||
// clear background
|
||||
UIColor.clear.setFill()
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
// draw elephantThreeOnGrassWithTreeTwoImage
|
||||
// elephantThreeOnGrassWithTreeTwo.bottomY - 25 align to elephantThreeOnGrassImage.centerY
|
||||
elephantThreeOnGrassWithTreeTwoImage.draw(at: CGPoint(x: width - elephantThreeOnGrassWithTreeTwoImage.size.width, y: height - 0.5 * elephantThreeOnGrassImage.size.height - elephantThreeOnGrassWithTreeTwoImage.size.height - 20))
|
||||
}
|
||||
|
||||
leftHillImageView.image = UIGraphicsImageRenderer(size: size).image { context in
|
||||
// clear background
|
||||
UIColor.clear.setFill()
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
// draw elephantThreeOnGrassWithTreeThree
|
||||
// elephantThreeOnGrassWithTreeThree.bottomY + 30 align to elephantThreeOnGrassImage.centerY
|
||||
elephantThreeOnGrassWithTreeThreeImage.draw(at: CGPoint(x: -160, y: height - 0.5 * elephantThreeOnGrassImage.size.height - elephantThreeOnGrassWithTreeThreeImage.size.height - 80))
|
||||
}
|
||||
|
||||
centerHillImageView.image = UIGraphicsImageRenderer(size: size).image { context in
|
||||
// clear background
|
||||
UIColor.clear.setFill()
|
||||
context.fill(CGRect(origin: .zero, size: size))
|
||||
|
||||
// draw elephantThreeOnGrass
|
||||
elephantThreeOnGrassExtendImage.draw(at: CGPoint(x: 0, y: height - elephantThreeOnGrassExtendImage.size.height))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if canImport(SwiftUI) && DEBUG
|
||||
|
@ -140,13 +217,17 @@ struct WelcomeIllustrationView_Previews: PreviewProvider {
|
|||
static var previews: some View {
|
||||
Group {
|
||||
UIViewPreview(width: 375) {
|
||||
WelcomeIllustrationView()
|
||||
let view = WelcomeIllustrationView()
|
||||
view.layout = .compact
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 1500))
|
||||
UIViewPreview(width: 1125) {
|
||||
WelcomeIllustrationView()
|
||||
UIViewPreview(width: 547) {
|
||||
let view = WelcomeIllustrationView()
|
||||
view.layout = .regular
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 1125, height: 5000))
|
||||
.previewLayout(.fixed(width: 547, height: 1500))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import Combine
|
|||
|
||||
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) } }
|
||||
|
||||
|
@ -41,30 +43,36 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
|||
return label
|
||||
}()
|
||||
|
||||
let buttonContainer = UIStackView()
|
||||
|
||||
private(set) lazy var signUpButton: PrimaryActionButton = {
|
||||
let button = PrimaryActionButton()
|
||||
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
|
||||
button.setTitle(L10n.Common.Controls.Actions.signUp, for: .normal)
|
||||
button.setTitle("Get Started", for: .normal) // TODO: i18n
|
||||
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)
|
||||
let titleColor: UIColor = Asset.Colors.brandBlue.color
|
||||
button.setTitleColor(titleColor, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitleColor(.black, for: .normal)
|
||||
return button
|
||||
}()
|
||||
|
||||
private(set) lazy var signInButton: UIButton = {
|
||||
let button = UIButton(type: .system)
|
||||
let button = PrimaryActionButton()
|
||||
button.adjustsBackgroundImageWhenUserInterfaceStyleChanges = false
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
||||
button.setTitle(L10n.Common.Controls.Actions.signIn, for: .normal)
|
||||
button.setTitle("Log In", 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.8)
|
||||
button.setTitleColor(titleColor, for: .normal)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
private(set) lazy var gradientBorderView = GradientBorderView(frame: view.bounds)
|
||||
|
||||
deinit {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
}
|
||||
|
@ -76,7 +84,8 @@ extension WelcomeViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// preferredContentSize = CGSize(width: 547, height: 678)
|
||||
definesPresentationContext = true
|
||||
preferredContentSize = CGSize(width: 547, height: 678)
|
||||
|
||||
navigationController?.navigationBar.prefersLargeTitles = true
|
||||
navigationItem.largeTitleDisplayMode = .never
|
||||
|
@ -84,19 +93,37 @@ extension WelcomeViewController {
|
|||
|
||||
setupOnboardingAppearance()
|
||||
setupIllustrationLayout()
|
||||
|
||||
view.addSubview(signInButton)
|
||||
view.addSubview(signUpButton)
|
||||
|
||||
buttonContainer.axis = .vertical
|
||||
buttonContainer.spacing = 12
|
||||
buttonContainer.isLayoutMarginsRelativeArrangement = true
|
||||
|
||||
buttonContainer.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(buttonContainer)
|
||||
NSLayoutConstraint.activate([
|
||||
signInButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: signInButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||
view.layoutMarginsGuide.bottomAnchor.constraint(equalTo: signInButton.bottomAnchor, constant: WelcomeViewController.viewBottomPaddingHeight),
|
||||
signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh),
|
||||
|
||||
signInButton.topAnchor.constraint(equalTo: signUpButton.bottomAnchor, constant: 9),
|
||||
signUpButton.leadingAnchor.constraint(equalTo: view.readableContentGuide.leadingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||
view.readableContentGuide.trailingAnchor.constraint(equalTo: signUpButton.trailingAnchor, constant: WelcomeViewController.actionButtonMargin),
|
||||
signUpButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.defaultHigh),
|
||||
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(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
|
||||
])
|
||||
signInButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
buttonContainer.addArrangedSubview(signInButton)
|
||||
NSLayoutConstraint.activate([
|
||||
signInButton.heightAnchor.constraint(equalToConstant: WelcomeViewController.actionButtonHeight).priority(.required - 1),
|
||||
])
|
||||
|
||||
gradientBorderView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(gradientBorderView)
|
||||
NSLayoutConstraint.activate([
|
||||
gradientBorderView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
gradientBorderView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
gradientBorderView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
gradientBorderView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
||||
signUpButton.addTarget(self, action: #selector(signUpButtonDidClicked(_:)), for: .touchUpInside)
|
||||
|
@ -109,17 +136,6 @@ extension WelcomeViewController {
|
|||
self.navigationItem.leftBarButtonItem = needsShowDismissEntry ? self.dismissBarButtonItem : nil
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
view.observe(\.frame, options: [.initial, .new]) { [weak self] view, _ in
|
||||
guard let self = self else { return }
|
||||
switch view.traitCollection.userInterfaceIdiom {
|
||||
case .phone:
|
||||
break
|
||||
default:
|
||||
self.welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.isHidden = view.frame.height < 800
|
||||
}
|
||||
}
|
||||
.store(in: &observations)
|
||||
}
|
||||
|
||||
override func viewSafeAreaInsetsDidChange() {
|
||||
|
@ -130,18 +146,49 @@ extension WelcomeViewController {
|
|||
if view.safeAreaInsets.bottom == 0 {
|
||||
overlap += 56
|
||||
}
|
||||
// shift illustration down for iPad modal
|
||||
if UIDevice.current.userInterfaceIdiom != .phone {
|
||||
overlap += 20
|
||||
}
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint?.constant = overlap
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
setupIllustrationLayout()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WelcomeViewController {
|
||||
|
||||
private func updateButtonContainerLayoutMargins(traitCollection: UITraitCollection) {
|
||||
switch traitCollection.userInterfaceIdiom {
|
||||
case .phone:
|
||||
buttonContainer.layoutMargins = UIEdgeInsets(
|
||||
top: 0,
|
||||
left: WelcomeViewController.actionButtonMargin,
|
||||
bottom: WelcomeViewController.viewBottomPaddingHeight,
|
||||
right: WelcomeViewController.actionButtonMargin
|
||||
)
|
||||
default:
|
||||
let margin = traitCollection.horizontalSizeClass == .regular ? WelcomeViewController.actionButtonMarginExtend : WelcomeViewController.actionButtonMargin
|
||||
buttonContainer.layoutMargins = UIEdgeInsets(
|
||||
top: 0,
|
||||
left: margin,
|
||||
bottom: WelcomeViewController.viewBottomPaddingHeightExtend,
|
||||
right: margin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupIllustrationLayout() {
|
||||
welcomeIllustrationView.layout = {
|
||||
switch traitCollection.userInterfaceIdiom {
|
||||
case .phone:
|
||||
return .compact
|
||||
default:
|
||||
return .regular
|
||||
}
|
||||
}()
|
||||
|
||||
// set logo
|
||||
if logoImageView.superview == nil {
|
||||
view.addSubview(logoImageView)
|
||||
|
@ -154,10 +201,11 @@ extension WelcomeViewController {
|
|||
logoImageView.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||
}
|
||||
|
||||
// set illustration for phone
|
||||
// set illustration
|
||||
guard welcomeIllustrationView.superview == nil else {
|
||||
return
|
||||
}
|
||||
welcomeIllustrationView.contentMode = .scaleAspectFit
|
||||
|
||||
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 5)
|
||||
|
@ -166,7 +214,7 @@ extension WelcomeViewController {
|
|||
NSLayoutConstraint.activate([
|
||||
view.leftAnchor.constraint(equalTo: welcomeIllustrationView.leftAnchor, constant: 15),
|
||||
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 15),
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint!
|
||||
welcomeIllustrationViewBottomAnchorLayoutConstraint!.priority(.required - 1),
|
||||
])
|
||||
|
||||
welcomeIllustrationView.cloudBaseImageView.addMotionEffect(
|
||||
|
@ -268,21 +316,34 @@ 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)")
|
||||
|
||||
updateButtonContainerLayoutMargins(traitCollection: traitCollection)
|
||||
|
||||
switch traitCollection.userInterfaceIdiom {
|
||||
case .phone:
|
||||
// make underneath view controller alive to fix layout issue due to view life cycle
|
||||
return .fullScreen
|
||||
default:
|
||||
return .formSheet
|
||||
// switch traitCollection.horizontalSizeClass {
|
||||
// case .regular:
|
||||
// default:
|
||||
// return .fullScreen
|
||||
// }
|
||||
switch traitCollection.horizontalSizeClass {
|
||||
case .compact:
|
||||
return .fullScreen
|
||||
case .regular:
|
||||
return .formSheet
|
||||
case .unspecified:
|
||||
return .formSheet
|
||||
@unknown default:
|
||||
return .formSheet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class WizardViewController: UIViewController {
|
|||
|
||||
let backgroundView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
|
||||
view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
||||
return view
|
||||
}()
|
||||
|
||||
|
|
Loading…
Reference in New Issue