Skip to content

Commit a0ebe53

Browse files
authored
Merge pull request #3867 from woocommerce/issue/3824-prologue-carousel
Login: New Prologue Screens
2 parents 2e1bc10 + 92b1a08 commit a0ebe53

File tree

28 files changed

+483
-131
lines changed

28 files changed

+483
-131
lines changed

Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ target 'WooCommerce' do
4242
pod 'Gridicons', '~> 1.0'
4343

4444
# To allow pod to pick up beta versions use -beta. E.g., 1.1.7-beta.1
45-
pod 'WordPressAuthenticator', '~> 1.35.0'
45+
pod 'WordPressAuthenticator', '~> 1.36.0-beta'
4646
# pod 'WordPressAuthenticator', :git => 'https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git', :commit => ''
4747
# pod 'WordPressAuthenticator', :git => 'https://github.com/wordpress-mobile/WordPressAuthenticator-iOS.git', :branch => ''
4848
# pod 'WordPressAuthenticator', :path => '../WordPressAuthenticator-iOS'

Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ PODS:
5454
- WordPress-Aztec-iOS (1.11.0)
5555
- WordPress-Editor-iOS (1.11.0):
5656
- WordPress-Aztec-iOS (= 1.11.0)
57-
- WordPressAuthenticator (1.35.0):
57+
- WordPressAuthenticator (1.36.0-beta.2):
5858
- 1PasswordExtension (~> 1.8.6)
5959
- Alamofire (~> 4.8)
6060
- CocoaLumberjack (~> 3.5)
@@ -107,7 +107,7 @@ DEPENDENCIES:
107107
- Kingfisher (~> 5.11.0)
108108
- Sourcery (~> 1.0.3)
109109
- WordPress-Editor-iOS (~> 1.11.0)
110-
- WordPressAuthenticator (~> 1.35.0)
110+
- WordPressAuthenticator (~> 1.36.0-beta)
111111
- WordPressKit (~> 4.26.0)
112112
- WordPressShared (~> 1.15)
113113
- WordPressUI (~> 1.7.2)
@@ -184,7 +184,7 @@ SPEC CHECKSUMS:
184184
UIDeviceIdentifier: a79ccdfc940373835a7d8e9fc7541e6bf61b6319
185185
WordPress-Aztec-iOS: 050b34d4c3adfb7c60363849049b13d60683b348
186186
WordPress-Editor-iOS: 304098424f1051cb271546c99f906aac296b1b81
187-
WordPressAuthenticator: f65b518f15c6786b44e6771a0a2bfffce8a1967a
187+
WordPressAuthenticator: 973d212f3ee787f833fa645c36baa432c671333b
188188
WordPressKit: 085ae5368e94a552e485942c6cee7c2d8bf4749d
189189
WordPressShared: e1915cf3b4ac33f41c47610693b9ca30c0cdab45
190190
WordPressUI: a50ea1786cb75330c8cfafe0c97a88db8d796699
@@ -200,6 +200,6 @@ SPEC CHECKSUMS:
200200
ZendeskSupportProvidersSDK: e183d32abac888c448469e2005c4a5a8c3ed73f0
201201
ZendeskSupportSDK: e52f37fa8bcba91f024b81025869fe5a2860f741
202202

203-
PODFILE CHECKSUM: 26825278dc7d4618787dfc7e4608ff6798281c10
203+
PODFILE CHECKSUM: bb7678b42e07ca00a2a0637065557be62427652c
204204

205205
COCOAPODS: 1.10.0

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
6.4
44
-----
5+
- [*] Login: New design and illustrations for the initial login screen, promoting the app's main features. [https://github.com/woocommerce/woocommerce-ios/pull/3867]
56
- [*] Enhancement/fix: Unify back button style across the app. [https://github.com/woocommerce/woocommerce-ios/pull/3872]
67

78
6.3
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import UIKit
2+
3+
/// Displays the Login Prologue carousel, populated with `LoginProloguePageTypeViewController` pages.
4+
///
5+
final class LoginProloguePageViewController: UIPageViewController {
6+
7+
private let pages: [UIViewController] = {
8+
LoginProloguePageType.allCases.map { LoginProloguePageTypeViewController(pageType: $0) }
9+
}()
10+
11+
private let pageControl = UIPageControl()
12+
13+
init() {
14+
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal)
15+
}
16+
17+
required init?(coder: NSCoder) {
18+
fatalError("init(coder:) has not been implemented")
19+
}
20+
21+
override func viewDidLoad() {
22+
super.viewDidLoad()
23+
dataSource = self
24+
delegate = self
25+
26+
if let firstPage = pages.first {
27+
setViewControllers([firstPage], direction: .forward, animated: false)
28+
}
29+
30+
addPageControl()
31+
}
32+
33+
// MARK: Page Control Setup
34+
//
35+
private func addPageControl() {
36+
pageControl.currentPageIndicatorTintColor = .gray(.shade5)
37+
pageControl.pageIndicatorTintColor = .wooCommercePurple(.shade50)
38+
pageControl.transform = CGAffineTransform(scaleX: Constants.pageControlScale, y: Constants.pageControlScale)
39+
40+
pageControl.translatesAutoresizingMaskIntoConstraints = false
41+
view.addSubview(pageControl)
42+
43+
NSLayoutConstraint.activate([
44+
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
45+
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: Constants.pageControlBottomMargin)
46+
])
47+
48+
pageControl.numberOfPages = pages.count
49+
pageControl.addTarget(self, action: #selector(handlePageControlValueChanged(sender:)), for: .valueChanged)
50+
}
51+
52+
@objc func handlePageControlValueChanged(sender: UIPageControl) {
53+
guard let currentPage = viewControllers?.first,
54+
let currentIndex = pages.firstIndex(of: currentPage) else {
55+
return
56+
}
57+
58+
let direction: UIPageViewController.NavigationDirection = sender.currentPage > currentIndex ? .forward : .reverse
59+
setViewControllers([pages[sender.currentPage]], direction: direction, animated: true)
60+
}
61+
}
62+
63+
// MARK: - UIPageViewControllerDataSource Conformance
64+
//
65+
extension LoginProloguePageViewController: UIPageViewControllerDataSource {
66+
67+
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
68+
guard let index = pages.firstIndex(of: viewController),
69+
index > 0 else {
70+
return nil
71+
}
72+
73+
return pages[index - 1]
74+
}
75+
76+
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
77+
guard let index = pages.firstIndex(of: viewController),
78+
index < pages.count - 1 else {
79+
return nil
80+
}
81+
82+
return pages[index + 1]
83+
}
84+
}
85+
86+
// MARK: - UIPageViewControllerDelegate Conformance
87+
//
88+
extension LoginProloguePageViewController: UIPageViewControllerDelegate {
89+
func pageViewController(_ pageViewController: UIPageViewController,
90+
didFinishAnimating finished: Bool,
91+
previousViewControllers: [UIViewController],
92+
transitionCompleted completed: Bool) {
93+
guard let toVC = previousViewControllers.first,
94+
let index = pages.firstIndex(of: toVC) else {
95+
return
96+
}
97+
if !completed {
98+
pageControl.currentPage = index
99+
}
100+
}
101+
102+
func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
103+
guard let toVC = pendingViewControllers.first,
104+
let index = pages.firstIndex(of: toVC) else {
105+
return
106+
}
107+
pageControl.currentPage = index
108+
}
109+
}
110+
111+
// MARK: - Constants
112+
private extension LoginProloguePageViewController {
113+
enum Constants {
114+
static let pageControlBottomMargin: CGFloat = -10
115+
static let pageControlScale: CGFloat = 0.8 // Scales page control according to design
116+
}
117+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import UIKit
2+
3+
// MARK: - Contents
4+
5+
/// Details for each page of the login prologue carousel.
6+
///
7+
enum LoginProloguePageType: CaseIterable {
8+
case stats
9+
case orderManagement
10+
case products
11+
case reviews
12+
13+
var title: String {
14+
switch self {
15+
case .stats:
16+
return NSLocalizedString("Track sales and high performing products",
17+
comment: "Caption displayed in promotional screens shown during the login flow.")
18+
case .orderManagement:
19+
return NSLocalizedString("Manage your store orders on the go ",
20+
comment: "Caption displayed in promotional screens shown during the login flow.")
21+
case .products:
22+
return NSLocalizedString("Edit and add new products from anywhere",
23+
comment: "Caption displayed in promotional screens shown during the login flow.")
24+
case .reviews:
25+
return NSLocalizedString("Monitor and approve your product reviews",
26+
comment: "Caption displayed in promotional screens shown during the login flow.")
27+
}
28+
}
29+
30+
var image: UIImage {
31+
switch self {
32+
case .stats:
33+
return UIImage.prologueAnalyticsImage
34+
case .orderManagement:
35+
return UIImage.prologueOrdersImage
36+
case .products:
37+
return UIImage.prologueProductsImage
38+
case .reviews:
39+
return UIImage.prologueReviewsImage
40+
}
41+
}
42+
}
43+
44+
// MARK: - View Controller
45+
46+
/// Simple container for each page of the login prologue carousel.
47+
///
48+
class LoginProloguePageTypeViewController: UIViewController {
49+
private let stackView = UIStackView()
50+
private let titleLabel = UILabel()
51+
private let imageView = UIImageView()
52+
53+
private var pageType: LoginProloguePageType
54+
55+
init(pageType: LoginProloguePageType) {
56+
self.pageType = pageType
57+
58+
super.init(nibName: nil, bundle: nil)
59+
}
60+
61+
required init?(coder: NSCoder) {
62+
fatalError("init(coder:) has not been implemented")
63+
}
64+
65+
override func viewDidLoad() {
66+
view = UIView()
67+
view.backgroundColor = .clear
68+
69+
configureStackView()
70+
configureImage()
71+
configureTitle()
72+
}
73+
74+
private func configureStackView() {
75+
view.addSubview(stackView)
76+
77+
// Stack view layout
78+
stackView.axis = .vertical
79+
stackView.alignment = .center
80+
stackView.spacing = Constants.stackSpacing
81+
82+
// Reduce centerYAnchor constraint priority to ensure the bottom margin has higher priority, so stack view is fully visible on shorter devices
83+
let verticalCentering = stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: Constants.stackVerticalOffset)
84+
verticalCentering.priority = .required - 1
85+
86+
// Set constraints
87+
stackView.translatesAutoresizingMaskIntoConstraints = false
88+
NSLayoutConstraint.activate([
89+
verticalCentering,
90+
stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: Constants.stackBottomMargin),
91+
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
92+
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
93+
])
94+
}
95+
96+
private func configureImage() {
97+
stackView.addArrangedSubview(imageView)
98+
99+
// Image style & layout
100+
imageView.contentMode = .scaleAspectFit
101+
imageView.translatesAutoresizingMaskIntoConstraints = false
102+
NSLayoutConstraint.activate([
103+
imageView.widthAnchor.constraint(equalTo: stackView.widthAnchor),
104+
imageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: Constants.imageHeightMultiplier)
105+
])
106+
107+
// Image contents
108+
imageView.image = pageType.image
109+
}
110+
111+
private func configureTitle() {
112+
stackView.addArrangedSubview(titleLabel)
113+
114+
// Label style & layout
115+
titleLabel.font = .body
116+
titleLabel.adjustsFontForContentSizeCategory = true
117+
titleLabel.textColor = .text
118+
titleLabel.textAlignment = .center
119+
titleLabel.numberOfLines = 0
120+
titleLabel.translatesAutoresizingMaskIntoConstraints = false
121+
titleLabel.setContentHuggingPriority(.required, for: .vertical)
122+
titleLabel.setContentCompressionResistancePriority(.required, for: .vertical)
123+
NSLayoutConstraint.activate([
124+
titleLabel.widthAnchor.constraint(equalToConstant: Constants.labelWidth)
125+
])
126+
127+
// Label contents
128+
titleLabel.text = pageType.title
129+
}
130+
131+
private enum Constants {
132+
static let stackSpacing: CGFloat = 40 // Space between image and text
133+
static let stackVerticalOffset: CGFloat = 103
134+
static let stackBottomMargin: CGFloat = -57 // Minimum margin between stack view and login buttons, including space required for UIPageControl
135+
static let imageHeightMultiplier: CGFloat = 0.35
136+
static let labelWidth: CGFloat = 216
137+
}
138+
}

0 commit comments

Comments
 (0)