feat: draw welcome illustration

This commit is contained in:
CMK 2021-03-01 17:38:45 +08:00
parent 25c3d6e74d
commit 47abbadaba
10 changed files with 173 additions and 23 deletions

View File

@ -69,6 +69,19 @@ internal enum Asset {
internal static let systemOrange = ColorAsset(name: "Colors/system.orange") internal static let systemOrange = ColorAsset(name: "Colors/system.orange")
} }
internal enum Welcome { internal enum Welcome {
internal enum Illustration {
internal static let backgroundCyan = ColorAsset(name: "Welcome/illustration/background.cyan")
internal static let cloudBase = ImageAsset(name: "Welcome/illustration/cloud.base")
internal static let cloudFirst = ImageAsset(name: "Welcome/illustration/cloud.first")
internal static let cloudSecond = ImageAsset(name: "Welcome/illustration/cloud.second")
internal static let cloudThird = ImageAsset(name: "Welcome/illustration/cloud.third")
internal static let elephantFourOnGrassWithTreeTwo = ImageAsset(name: "Welcome/illustration/elephant.four.on.grass.with.tree.two")
internal static let elephantOnAirplaneWithContrail = ImageAsset(name: "Welcome/illustration/elephant.on.airplane.with.contrail")
internal static let elephantThreeOnGrass = ImageAsset(name: "Welcome/illustration/elephant.three.on.grass")
internal static let elephantThreeOnGrassWithTreeFour = ImageAsset(name: "Welcome/illustration/elephant.three.on.grass.with.tree.four")
internal static let elephantTwo = ImageAsset(name: "Welcome/illustration/elephant.two")
internal static let lineDashTwo = ImageAsset(name: "Welcome/illustration/line.dash.two")
}
internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo") internal static let mastodonLogo = ImageAsset(name: "Welcome/mastodon.logo")
internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large") internal static let mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large")
} }

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "Untitled-1_0008_Group-3.png", "filename" : "Untitled-1_0010_Group-5.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

View File

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "Untitled-1_0010_Group-5.png", "filename" : "Untitled-1_0009_Group-4.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -9,6 +9,31 @@ import UIKit
final class WelcomeIllustrationView: UIView { final class WelcomeIllustrationView: UIView {
static let artworkImageSize = CGSize(width: 870, height: 2000)
let artworkImageView = UIImageView()
// layout outside
let elephantOnAirplaneWithContrailImageView: UIImageView = {
let imageView = UIImageView(image: Asset.Welcome.Illustration.elephantOnAirplaneWithContrail.image)
imageView.contentMode = .scaleAspectFill
return imageView
}()
let cloudFirstImageView: UIImageView = {
let imageView = UIImageView(image: Asset.Welcome.Illustration.cloudFirst.image)
imageView.contentMode = .scaleAspectFill
return imageView
}()
let cloudSecondImageView: UIImageView = {
let imageView = UIImageView(image: Asset.Welcome.Illustration.cloudSecond.image)
imageView.contentMode = .scaleAspectFill
return imageView
}()
let cloudThirdImageView: UIImageView = {
let imageView = UIImageView(image: Asset.Welcome.Illustration.cloudThird.image)
imageView.contentMode = .scaleAspectFill
return imageView
}()
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
_init() _init()
@ -23,7 +48,77 @@ final class WelcomeIllustrationView: UIView {
extension WelcomeIllustrationView { extension WelcomeIllustrationView {
private func _init() { private func _init() {
backgroundColor = Asset.Welcome.Illustration.backgroundCyan.color
let topPaddingView = UIView()
topPaddingView.translatesAutoresizingMaskIntoConstraints = false
addSubview(topPaddingView)
NSLayoutConstraint.activate([
topPaddingView.topAnchor.constraint(equalTo: topAnchor),
topPaddingView.leadingAnchor.constraint(equalTo: leadingAnchor),
topPaddingView.trailingAnchor.constraint(equalTo: trailingAnchor),
])
artworkImageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(artworkImageView)
NSLayoutConstraint.activate([
artworkImageView.topAnchor.constraint(equalTo: topPaddingView.bottomAnchor),
artworkImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
artworkImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
artworkImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
artworkImageView.widthAnchor.constraint(equalTo: artworkImageView.heightAnchor, multiplier: WelcomeIllustrationView.artworkImageSize.width / WelcomeIllustrationView.artworkImageSize.height),
])
}
override func layoutSubviews() {
super.layoutSubviews()
artworkImageView.image = WelcomeIllustrationView.bottomPartImage()
}
static func bottomPartImage() -> UIImage {
let size = artworkImageSize
let width = artworkImageSize.width
let height = artworkImageSize.height
let image = UIGraphicsImageRenderer(size: size).image { context in
// clear background
UIColor.clear.setFill()
context.fill(CGRect(origin: .zero, size: artworkImageSize))
// draw cloud
let cloudBaseImage = Asset.Welcome.Illustration.cloudBase.image
cloudBaseImage.draw(at: CGPoint(x: 0, y: height - cloudBaseImage.size.height))
let elephantFourOnGrassWithTreeTwoImage = Asset.Welcome.Illustration.elephantFourOnGrassWithTreeTwo.image
let elephantThreeOnGrassWithTreeFourImage = Asset.Welcome.Illustration.elephantThreeOnGrassWithTreeFour.image
let elephantThreeOnGrassImage = Asset.Welcome.Illustration.elephantThreeOnGrass.image
let elephantTwoImage = Asset.Welcome.Illustration.elephantTwo.image
let ineDashTwoImage = Asset.Welcome.Illustration.lineDashTwo.image
let elephantOnAirplaneWithContrailImageView = Asset.Welcome.Illustration.elephantOnAirplaneWithContrail.image
// draw elephantFourOnGrassWithTreeTwo
// elephantFourOnGrassWithTreeTwo.bottomY + 40 align to elephantThreeOnGrassImage.centerY
elephantFourOnGrassWithTreeTwoImage.draw(at: CGPoint(x: 0, y: height - 0.5 * elephantThreeOnGrassImage.size.height - elephantFourOnGrassWithTreeTwoImage.size.height - 40))
// draw elephantThreeOnGrassWithTreeFour
// elephantThreeOnGrassWithTreeFour.bottomY + 40 align to elephantThreeOnGrassImage.centerY
elephantThreeOnGrassWithTreeFourImage.draw(at: CGPoint(x: width - elephantThreeOnGrassWithTreeFourImage.size.width, y: height - 0.5 * elephantThreeOnGrassImage.size.height - elephantThreeOnGrassWithTreeFourImage.size.height - 40))
// draw elephantThreeOnGrass
elephantThreeOnGrassImage.draw(at: CGPoint(x: 0, y: height - elephantThreeOnGrassImage.size.height))
// darw ineDashTwoImage
ineDashTwoImage.draw(at: CGPoint(x: 0.5 * elephantThreeOnGrassImage.size.width + 60, y: height - elephantThreeOnGrassImage.size.height - 50))
// draw elephantTwo.image
elephantTwoImage.draw(at: CGPoint(x: 0, y: height - elephantTwoImage.size.height - 125))
// draw elephantOnAirplaneWithContrailImageView
elephantOnAirplaneWithContrailImageView.draw(at: CGPoint(x: 0, y: height - cloudBaseImage.size.height - 0.5 * elephantOnAirplaneWithContrailImageView.size.height))
}
return image
} }
} }
@ -33,10 +128,20 @@ import SwiftUI
struct WelcomeIllustrationView_Previews: PreviewProvider { struct WelcomeIllustrationView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
UIViewPreview(width: 375) { Group {
WelcomeIllustrationView() UIViewPreview(width: 870) {
WelcomeIllustrationView()
}
.previewLayout(.fixed(width: 870, height: 2000))
UIViewPreview(width: 375) {
WelcomeIllustrationView()
}
.previewLayout(.fixed(width: 375, height: 812))
UIViewPreview(width: 428) {
WelcomeIllustrationView()
}
.previewLayout(.fixed(width: 428, height: 926))
} }
.previewLayout(.fixed(width: 375, height: 812))
} }
} }

View File

@ -13,6 +13,9 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
let welcomeIllustrationView = WelcomeIllustrationView()
var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint!
private(set) lazy var logoImageView: UIImageView = { private(set) lazy var logoImageView: UIImageView = {
let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Welcome.mastodonLogo.image : Asset.Welcome.mastodonLogoLarge.image let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Welcome.mastodonLogo.image : Asset.Welcome.mastodonLogoLarge.image
let imageView = UIImageView(image: image) let imageView = UIImageView(image: image)
@ -42,7 +45,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
let button = UIButton(type: .system) let button = UIButton(type: .system)
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold)) button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
button.setTitle(L10n.Common.Controls.Actions.signIn, for: .normal) button.setTitle(L10n.Common.Controls.Actions.signIn, for: .normal)
button.setTitleColor(Asset.Colors.lightBrandBlue.color, for: .normal) button.setTitleColor(UIColor.white.withAlphaComponent(0.8), for: .normal)
button.setInsets(forContentPadding: UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0), imageTitlePadding: 0) button.setInsets(forContentPadding: UIEdgeInsets(top: 12, left: 0, bottom: 12, right: 0), imageTitlePadding: 0)
button.translatesAutoresizingMaskIntoConstraints = false button.translatesAutoresizingMaskIntoConstraints = false
return button return button
@ -60,6 +63,16 @@ extension WelcomeViewController {
super.viewDidLoad() super.viewDidLoad()
setupOnboardingAppearance() setupOnboardingAppearance()
view.backgroundColor = Asset.Welcome.Illustration.backgroundCyan.color
welcomeIllustrationView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(welcomeIllustrationView)
welcomeIllustrationViewBottomAnchorLayoutConstraint = welcomeIllustrationView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
NSLayoutConstraint.activate([
welcomeIllustrationView.leftAnchor.constraint(equalTo: view.leftAnchor),
welcomeIllustrationView.rightAnchor.constraint(equalTo: view.rightAnchor),
welcomeIllustrationViewBottomAnchorLayoutConstraint,
])
view.addSubview(logoImageView) view.addSubview(logoImageView)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -76,6 +89,19 @@ extension WelcomeViewController {
sloganLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 168), sloganLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 168),
]) ])
welcomeIllustrationView.cloudFirstImageView.translatesAutoresizingMaskIntoConstraints = false
welcomeIllustrationView.cloudSecondImageView.translatesAutoresizingMaskIntoConstraints = false
welcomeIllustrationView.cloudFirstImageView.translatesAutoresizingMaskIntoConstraints = false
// welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.translatesAutoresizingMaskIntoConstraints = false
// view.addSubview(welcomeIllustrationView.elephantOnAirplaneWithContrailImageView)
// NSLayoutConstraint.activate([
// welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.leftAnchor.constraint(equalTo: view.leftAnchor),
// welcomeIllustrationView.elephantOnAirplaneWithContrailImageView.bottomAnchor.constraint(equalTo: sloganLabel.topAnchor),
// ])
// welcomeIllustrationView.welcomeIllustrationView.sca
// view.bringSubviewToFront(sloganLabel)
view.addSubview(signInButton) view.addSubview(signInButton)
view.addSubview(signUpButton) view.addSubview(signUpButton)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
@ -94,7 +120,13 @@ extension WelcomeViewController {
signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside) signInButton.addTarget(self, action: #selector(signInButtonDidClicked(_:)), for: .touchUpInside)
} }
override var preferredStatusBarStyle: UIStatusBarStyle { return .darkContent } override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
// make illustration bottom over the bleeding
let overlap: CGFloat = 100
welcomeIllustrationViewBottomAnchorLayoutConstraint.constant = overlap - view.safeAreaInsets.bottom
}
} }

View File

@ -287,11 +287,11 @@ struct MosaicImageView_Previews: PreviewProvider {
UIViewPreview(width: 375) { UIViewPreview(width: 375) {
let view = MosaicImageViewContainer() let view = MosaicImageViewContainer()
let image = images[3] let image = images[3]
let imageView = view.setupImageView( let artworkImageView = view.setupImageView(
aspectRatio: image.size, aspectRatio: image.size,
maxSize: CGSize(width: 375, height: 400) maxSize: CGSize(width: 375, height: 400)
) )
imageView.image = image artworkImageView.image = image
return view return view
} }
.previewLayout(.fixed(width: 375, height: 400)) .previewLayout(.fixed(width: 375, height: 400))
@ -299,14 +299,14 @@ struct MosaicImageView_Previews: PreviewProvider {
UIViewPreview(width: 375) { UIViewPreview(width: 375) {
let view = MosaicImageViewContainer() let view = MosaicImageViewContainer()
let image = images[1] let image = images[1]
let imageView = view.setupImageView( let artworkImageView = view.setupImageView(
aspectRatio: image.size, aspectRatio: image.size,
maxSize: CGSize(width: 375, height: 400) maxSize: CGSize(width: 375, height: 400)
) )
imageView.layer.masksToBounds = true artworkImageView.layer.masksToBounds = true
imageView.layer.cornerRadius = 8 artworkImageView.layer.cornerRadius = 8
imageView.contentMode = .scaleAspectFill artworkImageView.contentMode = .scaleAspectFill
imageView.image = image artworkImageView.image = image
return view return view
} }
.previewLayout(.fixed(width: 375, height: 400)) .previewLayout(.fixed(width: 375, height: 400))
@ -315,8 +315,8 @@ struct MosaicImageView_Previews: PreviewProvider {
let view = MosaicImageViewContainer() let view = MosaicImageViewContainer()
let images = self.images.prefix(2) let images = self.images.prefix(2)
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162) let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
for (i, imageView) in imageViews.enumerated() { for (i, artworkImageView) in imageViews.enumerated() {
imageView.image = images[i] artworkImageView.image = images[i]
} }
return view return view
} }
@ -326,8 +326,8 @@ struct MosaicImageView_Previews: PreviewProvider {
let view = MosaicImageViewContainer() let view = MosaicImageViewContainer()
let images = self.images.prefix(3) let images = self.images.prefix(3)
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162) let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
for (i, imageView) in imageViews.enumerated() { for (i, artworkImageView) in imageViews.enumerated() {
imageView.image = images[i] artworkImageView.image = images[i]
} }
return view return view
} }
@ -337,8 +337,8 @@ struct MosaicImageView_Previews: PreviewProvider {
let view = MosaicImageViewContainer() let view = MosaicImageViewContainer()
let images = self.images.prefix(4) let images = self.images.prefix(4)
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162) let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
for (i, imageView) in imageViews.enumerated() { for (i, artworkImageView) in imageViews.enumerated() {
imageView.image = images[i] artworkImageView.image = images[i]
} }
return view return view
} }

View File

@ -358,8 +358,8 @@ struct StatusView_Previews: PreviewProvider {
statusView.updateContentWarningDisplay(isHidden: false) statusView.updateContentWarningDisplay(isHidden: false)
let images = MosaicImageView_Previews.images let images = MosaicImageView_Previews.images
let imageViews = statusView.statusMosaicImageView.setupImageViews(count: 4, maxHeight: 162) let imageViews = statusView.statusMosaicImageView.setupImageViews(count: 4, maxHeight: 162)
for (i, imageView) in imageViews.enumerated() { for (i, artworkImageView) in imageViews.enumerated() {
imageView.image = images[i] artworkImageView.image = images[i]
} }
statusView.statusMosaicImageView.isHidden = false statusView.statusMosaicImageView.isHidden = false
return statusView return statusView