diff --git a/Localization/app.json b/Localization/app.json index 6d3b2fcc..3b39a713 100644 --- a/Localization/app.json +++ b/Localization/app.json @@ -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" } } -} \ No newline at end of file +} diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 895b4aca..bb7a8a4f 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -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 = ""; }; DB03F7F42689B782007B274C /* ComposeTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTableView.swift; sourceTree = ""; }; DB040ED026538E3C00BEE9D8 /* Trie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trie.swift; sourceTree = ""; }; + DB0617EA277EF3820030EE79 /* GradientBorderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientBorderView.swift; sourceTree = ""; }; DB084B5625CBC56C00F898ED /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = ""; }; DB0AC6FB25CD02E600D75117 /* APIService+Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Instance.swift"; sourceTree = ""; }; DB0C946A26A700AB0088FB11 /* MastodonUser+Property.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MastodonUser+Property.swift"; sourceTree = ""; }; @@ -2993,6 +2995,7 @@ children = ( DBABE3EB25ECAC4B00879EE5 /* WelcomeIllustrationView.swift */, DB4932B026F1FB5300EF46D4 /* WizardCardView.swift */, + DB0617EA277EF3820030EE79 /* GradientBorderView.swift */, ); path = View; sourceTree = ""; @@ -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; diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 55e94fa7..8bd09ebc 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 24 + 18 CoreDataStack.xcscheme_^#shared#^_ orderHint - 26 + 19 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -102,7 +102,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 27 + 21 MastodonIntents.xcscheme_^#shared#^_ @@ -122,7 +122,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 25 + 20 SuppressBuildableAutocreation diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index c10b60d4..d3591ab4 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -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 } diff --git a/Mastodon/Generated/Assets.swift b/Mastodon/Generated/Assets.swift index 906dd74e..5098d05f 100644 --- a/Mastodon/Generated/Assets.swift +++ b/Mastodon/Generated/Assets.swift @@ -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 { diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/Contents.json new file mode 100644 index 00000000..421e01a3 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/Contents.json @@ -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 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend.png new file mode 100644 index 00000000..3c8443c9 Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@2x.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@2x.png new file mode 100644 index 00000000..b03b6720 Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@2x.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@3x.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@3x.png new file mode 100644 index 00000000..f7747685 Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/cloud.base.extend.imageset/cloud.base.extend@3x.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/Contents.json b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/Contents.json new file mode 100644 index 00000000..9c3ea2de --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/Contents.json @@ -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 + } +} diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend.png new file mode 100644 index 00000000..97ef8df6 Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@2x.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@2x.png new file mode 100644 index 00000000..63580f3c Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@2x.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@3x.png b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@3x.png new file mode 100644 index 00000000..8799a731 Binary files /dev/null and b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/illustration/elephant.three.on.grass.extend.imageset/elephant.three.on.grass.extend@3x.png differ diff --git a/Mastodon/Resources/Assets.xcassets/Scene/Welcome/sign.in.button.background.colorset/Contents.json b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/sign.in.button.background.colorset/Contents.json new file mode 100644 index 00000000..7bf1f1e4 --- /dev/null +++ b/Mastodon/Resources/Assets.xcassets/Scene/Welcome/sign.in.button.background.colorset/Contents.json @@ -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 + } +} diff --git a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift index 17c4699e..2405f2a8 100644 --- a/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift +++ b/Mastodon/Scene/Onboarding/Share/OnboardingViewControllerAppearance.swift @@ -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 diff --git a/Mastodon/Scene/Onboarding/Welcome/View/GradientBorderView.swift b/Mastodon/Scene/Onboarding/Welcome/View/GradientBorderView.swift new file mode 100644 index 00000000..c9a4a0d7 --- /dev/null +++ b/Mastodon/Scene/Onboarding/Welcome/View/GradientBorderView.swift @@ -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 + } +} diff --git a/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift b/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift index f5d8c41c..cb7ac768 100644 --- a/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift +++ b/Mastodon/Scene/Onboarding/Welcome/View/WelcomeIllustrationView.swift @@ -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)) } } diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index bf33ea13..d450764e 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -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 } diff --git a/Mastodon/Scene/Wizard/WizardViewController.swift b/Mastodon/Scene/Wizard/WizardViewController.swift index 2678c712..9152e64f 100644 --- a/Mastodon/Scene/Wizard/WizardViewController.swift +++ b/Mastodon/Scene/Wizard/WizardViewController.swift @@ -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 }()