forked from zelo72/mastodon-ios
feat: draw welcome illustration
This commit is contained in:
parent
25c3d6e74d
commit
47abbadaba
|
@ -69,6 +69,19 @@ internal enum Asset {
|
|||
internal static let systemOrange = ColorAsset(name: "Colors/system.orange")
|
||||
}
|
||||
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 mastodonLogoLarge = ImageAsset(name: "Welcome/mastodon.logo.large")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Untitled-1_0008_Group-3.png",
|
||||
"filename" : "Untitled-1_0010_Group-5.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "Untitled-1_0010_Group-5.png",
|
||||
"filename" : "Untitled-1_0009_Group-4.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
|
@ -9,6 +9,31 @@ import UIKit
|
|||
|
||||
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) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
|
@ -23,7 +48,77 @@ final class WelcomeIllustrationView: UIView {
|
|||
|
||||
extension WelcomeIllustrationView {
|
||||
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 {
|
||||
|
||||
static var previews: some View {
|
||||
UIViewPreview(width: 375) {
|
||||
WelcomeIllustrationView()
|
||||
Group {
|
||||
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))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
|||
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||
|
||||
let welcomeIllustrationView = WelcomeIllustrationView()
|
||||
var welcomeIllustrationViewBottomAnchorLayoutConstraint: NSLayoutConstraint!
|
||||
|
||||
private(set) lazy var logoImageView: UIImageView = {
|
||||
let image = view.traitCollection.userInterfaceIdiom == .phone ? Asset.Welcome.mastodonLogo.image : Asset.Welcome.mastodonLogoLarge.image
|
||||
let imageView = UIImageView(image: image)
|
||||
|
@ -42,7 +45,7 @@ final class WelcomeViewController: UIViewController, NeedsDependency {
|
|||
let button = UIButton(type: .system)
|
||||
button.titleLabel?.font = UIFontMetrics(forTextStyle: .headline).scaledFont(for: .systemFont(ofSize: 15, weight: .semibold))
|
||||
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.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
|
@ -60,6 +63,16 @@ extension WelcomeViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
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)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -76,6 +89,19 @@ extension WelcomeViewController {
|
|||
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(signUpButton)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -94,8 +120,14 @@ extension WelcomeViewController {
|
|||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension WelcomeViewController {
|
||||
|
|
|
@ -287,11 +287,11 @@ struct MosaicImageView_Previews: PreviewProvider {
|
|||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageViewContainer()
|
||||
let image = images[3]
|
||||
let imageView = view.setupImageView(
|
||||
let artworkImageView = view.setupImageView(
|
||||
aspectRatio: image.size,
|
||||
maxSize: CGSize(width: 375, height: 400)
|
||||
)
|
||||
imageView.image = image
|
||||
artworkImageView.image = image
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
|
@ -299,14 +299,14 @@ struct MosaicImageView_Previews: PreviewProvider {
|
|||
UIViewPreview(width: 375) {
|
||||
let view = MosaicImageViewContainer()
|
||||
let image = images[1]
|
||||
let imageView = view.setupImageView(
|
||||
let artworkImageView = view.setupImageView(
|
||||
aspectRatio: image.size,
|
||||
maxSize: CGSize(width: 375, height: 400)
|
||||
)
|
||||
imageView.layer.masksToBounds = true
|
||||
imageView.layer.cornerRadius = 8
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.image = image
|
||||
artworkImageView.layer.masksToBounds = true
|
||||
artworkImageView.layer.cornerRadius = 8
|
||||
artworkImageView.contentMode = .scaleAspectFill
|
||||
artworkImageView.image = image
|
||||
return view
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 400))
|
||||
|
@ -315,8 +315,8 @@ struct MosaicImageView_Previews: PreviewProvider {
|
|||
let view = MosaicImageViewContainer()
|
||||
let images = self.images.prefix(2)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
for (i, artworkImageView) in imageViews.enumerated() {
|
||||
artworkImageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
@ -326,8 +326,8 @@ struct MosaicImageView_Previews: PreviewProvider {
|
|||
let view = MosaicImageViewContainer()
|
||||
let images = self.images.prefix(3)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
for (i, artworkImageView) in imageViews.enumerated() {
|
||||
artworkImageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
@ -337,8 +337,8 @@ struct MosaicImageView_Previews: PreviewProvider {
|
|||
let view = MosaicImageViewContainer()
|
||||
let images = self.images.prefix(4)
|
||||
let imageViews = view.setupImageViews(count: images.count, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
for (i, artworkImageView) in imageViews.enumerated() {
|
||||
artworkImageView.image = images[i]
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -358,8 +358,8 @@ struct StatusView_Previews: PreviewProvider {
|
|||
statusView.updateContentWarningDisplay(isHidden: false)
|
||||
let images = MosaicImageView_Previews.images
|
||||
let imageViews = statusView.statusMosaicImageView.setupImageViews(count: 4, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
for (i, artworkImageView) in imageViews.enumerated() {
|
||||
artworkImageView.image = images[i]
|
||||
}
|
||||
statusView.statusMosaicImageView.isHidden = false
|
||||
return statusView
|
||||
|
|
Loading…
Reference in New Issue