From 54c8c1f352dc984dd02a6075c3bee0dfe62c3599 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 12 Feb 2019 14:56:58 -0500 Subject: [PATCH] Sketch out the onboarding splash view. --- Signal.xcodeproj/project.pbxproj | 8 ++ .../Contents.json | 21 ++++ .../Screen Shot 2019-02-12 at 2.22.35 PM.png | Bin 0 -> 36109 bytes .../OnboardingBaseViewController.swift | 75 +++++++++++++++ .../Registration/OnboardingController.swift | 18 +++- .../OnboardingPermissionsViewController.swift | 75 +++------------ .../OnboardingSplashViewController.swift | 91 ++++++++++++++++++ .../translations/en.lproj/Localizable.strings | 24 +++++ SignalMessaging/categories/UIView+OWS.swift | 14 +++ 9 files changed, 265 insertions(+), 61 deletions(-) create mode 100644 Signal/Images.xcassets/onboarding_splash_hero.imageset/Contents.json create mode 100644 Signal/Images.xcassets/onboarding_splash_hero.imageset/Screen Shot 2019-02-12 at 2.22.35 PM.png create mode 100644 Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift create mode 100644 Signal/src/ViewControllers/Registration/OnboardingSplashViewController.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index a194106e3..1e6fe7f26 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -74,6 +74,8 @@ 344825C6211390C800DB4BD8 /* OWSOrphanDataCleaner.m in Sources */ = {isa = PBXBuildFile; fileRef = 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */; }; 3448E15C22133274004B052E /* OnboardingPermissionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448E15B22133274004B052E /* OnboardingPermissionsViewController.swift */; }; 3448E15E221333F5004B052E /* OnboardingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448E15D221333F5004B052E /* OnboardingController.swift */; }; + 3448E16022134C89004B052E /* OnboardingSplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448E15F22134C88004B052E /* OnboardingSplashViewController.swift */; }; + 3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448E1612213585C004B052E /* OnboardingBaseViewController.swift */; }; 344F248D2007CCD600CFB4F4 /* DisplayableText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 344F248C2007CCD600CFB4F4 /* DisplayableText.swift */; }; 345BC30C2047030700257B7C /* OWS2FASettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 345BC30B2047030600257B7C /* OWS2FASettingsViewController.m */; }; 3461284B1FD0B94000532771 /* SAELoadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3461284A1FD0B93F00532771 /* SAELoadViewController.swift */; }; @@ -727,6 +729,8 @@ 344825C5211390C800DB4BD8 /* OWSOrphanDataCleaner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOrphanDataCleaner.m; sourceTree = ""; }; 3448E15B22133274004B052E /* OnboardingPermissionsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingPermissionsViewController.swift; sourceTree = ""; }; 3448E15D221333F5004B052E /* OnboardingController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingController.swift; sourceTree = ""; }; + 3448E15F22134C88004B052E /* OnboardingSplashViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingSplashViewController.swift; sourceTree = ""; }; + 3448E1612213585C004B052E /* OnboardingBaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingBaseViewController.swift; sourceTree = ""; }; 34491FC11FB0F78500B3E5A3 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = translations/my.lproj/Localizable.strings; sourceTree = ""; }; 344F248C2007CCD600CFB4F4 /* DisplayableText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayableText.swift; sourceTree = ""; }; 345BC30A2047030600257B7C /* OWS2FASettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWS2FASettingsViewController.h; sourceTree = ""; }; @@ -1443,8 +1447,10 @@ 3441FD9E21A3604F00BB9542 /* BackupRestoreViewController.swift */, 340FC879204DAC8C007AEB0F /* CodeVerificationViewController.h */, 340FC877204DAC8C007AEB0F /* CodeVerificationViewController.m */, + 3448E1612213585C004B052E /* OnboardingBaseViewController.swift */, 3448E15D221333F5004B052E /* OnboardingController.swift */, 3448E15B22133274004B052E /* OnboardingPermissionsViewController.swift */, + 3448E15F22134C88004B052E /* OnboardingSplashViewController.swift */, 346E9D5321B040B600562252 /* RegistrationController.swift */, 340FC878204DAC8C007AEB0F /* RegistrationViewController.h */, 340FC876204DAC8C007AEB0F /* RegistrationViewController.m */, @@ -3478,6 +3484,7 @@ D221A09A169C9E5E00537ABF /* main.m in Sources */, 3496957221A301A100DCFE74 /* OWSBackup.m in Sources */, 34B3F87B1E8DF1700035BE1A /* ExperienceUpgradesPageViewController.swift in Sources */, + 3448E1622213585C004B052E /* OnboardingBaseViewController.swift in Sources */, 34E5DC8220D8050D00C08145 /* RegistrationUtils.m in Sources */, 452EA09E1EA7ABE00078744B /* AttachmentPointerView.swift in Sources */, 45638BDC1F3DD0D400128435 /* DebugUICalling.swift in Sources */, @@ -3510,6 +3517,7 @@ 457C87B82032645C008D52D6 /* DebugUINotifications.swift in Sources */, 4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */, 4CC1ECFB211A553000CC13BE /* AppUpdateNag.swift in Sources */, + 3448E16022134C89004B052E /* OnboardingSplashViewController.swift in Sources */, 34B6A903218B3F63007C4606 /* TypingIndicatorView.swift in Sources */, 458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */, 34B6A905218B4C91007C4606 /* TypingIndicatorInteraction.swift in Sources */, diff --git a/Signal/Images.xcassets/onboarding_splash_hero.imageset/Contents.json b/Signal/Images.xcassets/onboarding_splash_hero.imageset/Contents.json new file mode 100644 index 000000000..2eb68d3be --- /dev/null +++ b/Signal/Images.xcassets/onboarding_splash_hero.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Screen Shot 2019-02-12 at 2.22.35 PM.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Signal/Images.xcassets/onboarding_splash_hero.imageset/Screen Shot 2019-02-12 at 2.22.35 PM.png b/Signal/Images.xcassets/onboarding_splash_hero.imageset/Screen Shot 2019-02-12 at 2.22.35 PM.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef277fb850fbe5c4fb308593fc2729d335bf4cf GIT binary patch literal 36109 zcmZU(18`+gw=Npnw%NfBI<{>)JGQlBI~^MxcWm3}*hvQ+TOB($|2gN~d#c{5T2*Ue zaAJLPj2We@D20qbfB*&thAbm3t_lVQ4)}Uq;9$OLil_DeeHDDI#l)0l#Kg#yT^uc} z?aaZz=n_qhjlan-(v1Ou#>Qh)4D<*to~n_Nv8u+sgMCwdgJeU-!(@3G`uf{=XxoE< zn7*AOeQu=L8zdwpY(j31YEBNK*%8

MvsweSNHnOzd0g?+W+60Ri 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 literal 0 HcmV?d00001 diff --git a/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift new file mode 100644 index 000000000..9f6546b09 --- /dev/null +++ b/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift @@ -0,0 +1,75 @@ +// +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// + +import UIKit +import PromiseKit + +@objc +public class OnboardingBaseViewController: OWSViewController { + // Unlike a delegate, we can and should retain a strong reference to the OnboardingController. + let onboardingController: OnboardingController + + @objc + public init(onboardingController: OnboardingController) { + self.onboardingController = onboardingController + + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable, message: "use other init() instead.") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + // MARK: - + + func titleLabel(text: String) -> UILabel { + let titleLabel = UILabel() + titleLabel.text = text + titleLabel.textColor = Theme.primaryColor + titleLabel.font = UIFont.ows_dynamicTypeTitle2.ows_mediumWeight() + titleLabel.numberOfLines = 0 + titleLabel.lineBreakMode = .byWordWrapping + titleLabel.textAlignment = .center + return titleLabel + } + + func explanationLabel(explanationText: String, linkText: String, selector: Selector) -> UILabel { + let explanationText = NSAttributedString(string: explanationText) + .rtlSafeAppend(NSAttributedString(string: " ")) + .rtlSafeAppend(linkText, + attributes: [ + NSAttributedStringKey.foregroundColor: UIColor.ows_materialBlue + ]) + let explanationLabel = UILabel() + explanationLabel.textColor = Theme.secondaryColor + explanationLabel.font = UIFont.ows_dynamicTypeCaption1 + explanationLabel.attributedText = explanationText + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + explanationLabel.isUserInteractionEnabled = true + explanationLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: selector)) + return explanationLabel + } + + func button(title: String, selector: Selector) -> UIView { + // TODO: Make sure this all fits if dynamic font sizes are maxed out. + let buttonHeight: CGFloat = 48 + let button = OWSFlatButton.button(title: title, + font: OWSFlatButton.fontForHeight(buttonHeight), + titleColor: .white, + backgroundColor: .ows_materialBlue, + target: self, + selector: selector) + button.autoSetDimension(.height, toSize: buttonHeight) + return button + } + + // MARK: Orientation + + public override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return .portrait + } +} diff --git a/Signal/src/ViewControllers/Registration/OnboardingController.swift b/Signal/src/ViewControllers/Registration/OnboardingController.swift index 8869bdda4..7514dc8b3 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingController.swift @@ -6,6 +6,10 @@ import UIKit @objc public protocol OnboardingController: class { + func initialViewController() -> UIViewController + + func onboardingSplashDidComplete(viewController: UIViewController) + func onboardingPermissionsWasSkipped(viewController: UIViewController) func onboardingPermissionsDidComplete(viewController: UIViewController) } @@ -13,8 +17,18 @@ public protocol OnboardingController: class { // MARK: - @objc -public class MockOnboardingController: NSObject, OnboardingController { +public class OnboardingControllerImpl: NSObject, OnboardingController { + public func initialViewController() -> UIViewController { + let view = OnboardingSplashViewController(onboardingController: self) + return view + } + + public func onboardingSplashDidComplete(viewController: UIViewController) { + let view = OnboardingPermissionsViewController(onboardingController: self) + viewController.navigationController?.pushViewController(view, animated: true) + } + public func onboardingPermissionsWasSkipped(viewController: UIViewController) {} - + public func onboardingPermissionsDidComplete(viewController: UIViewController) {} } diff --git a/Signal/src/ViewControllers/Registration/OnboardingPermissionsViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingPermissionsViewController.swift index 495d4b6c3..c19dd160d 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingPermissionsViewController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingPermissionsViewController.swift @@ -6,23 +6,7 @@ import UIKit import PromiseKit @objc -public class OnboardingPermissionsViewController: OWSViewController { - // Unlike a delegate, we can and should retain a strong reference to the OnboardingController. - private var onboardingController: OnboardingController - - @objc - public init(onboardingController: OnboardingController) { - self.onboardingController = onboardingController - - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable, message: "use other init() instead.") - required public init?(coder aDecoder: NSCoder) { - notImplemented() - } - - // MARK: - +public class OnboardingPermissionsViewController: OnboardingBaseViewController { override public func loadView() { super.loadView() @@ -38,46 +22,25 @@ public class OnboardingPermissionsViewController: OWSViewController { target: self, action: #selector(skipWasPressed)) - let titleLabel = UILabel() - titleLabel.text = NSLocalizedString("ONBOARDING_PERMISSIONS_TITLE", comment: "Title of the 'onboarding permissions' view.") - titleLabel.textColor = Theme.primaryColor - titleLabel.font = UIFont.ows_dynamicTypeTitle2.ows_mediumWeight() - titleLabel.numberOfLines = 0 - titleLabel.lineBreakMode = .byWordWrapping - titleLabel.textAlignment = .center + let titleLabel = self.titleLabel(text: NSLocalizedString("ONBOARDING_PERMISSIONS_TITLE", comment: "Title of the 'onboarding permissions' view.")) view.addSubview(titleLabel) titleLabel.autoPinEdges(toSuperviewMarginsExcludingEdge: .bottom) - let explainerLabel = UILabel() // TODO: Finalize copy. - explainerLabel.text = NSLocalizedString("ONBOARDING_PERMISSIONS_EXPLANATION", comment: "Explanation in the 'onboarding permissions' view.") - explainerLabel.textColor = Theme.secondaryColor - explainerLabel.font = UIFont.ows_dynamicTypeCaption1 - explainerLabel.numberOfLines = 0 - explainerLabel.textAlignment = .center - explainerLabel.lineBreakMode = .byWordWrapping - explainerLabel.isUserInteractionEnabled = true - explainerLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(explainerLabelTapped))) + let explanationLabel = self.explanationLabel(explanationText: NSLocalizedString("ONBOARDING_PERMISSIONS_EXPLANATION", + comment: "Explanation in the 'onboarding permissions' view."), + linkText: NSLocalizedString("ONBOARDING_PERMISSIONS_LEARN_MORE_LINK", + comment: "Link to the 'learn more' in the 'onboarding permissions' view."), + selector: #selector(explanationLabelTapped)) // TODO: Make sure this all fits if dynamic font sizes are maxed out. - let buttonHeight: CGFloat = 48 - let giveAccessButton = OWSFlatButton.button(title: NSLocalizedString("ONBOARDING_PERMISSIONS_GIVE_ACCESS_BUTTON", - comment: "Label for the 'give access' button in the 'onboarding permissions' view."), - font: OWSFlatButton.fontForHeight(buttonHeight), - titleColor: .white, - backgroundColor: .ows_materialBlue, - target: self, - selector: #selector(giveAccessPressed)) - giveAccessButton.autoSetDimension(.height, toSize: buttonHeight) - - let notNowButton = OWSFlatButton.button(title: NSLocalizedString("ONBOARDING_PERMISSIONS_GIVE_ACCESS_BUTTON", - comment: "Label for the 'give access' button in the 'onboarding permissions' view."), - font: OWSFlatButton.fontForHeight(buttonHeight), - titleColor: .white, - backgroundColor: .ows_materialBlue, - target: self, - selector: #selector(notNowPressed)) - notNowButton.autoSetDimension(.height, toSize: buttonHeight) + let giveAccessButton = self.button(title: NSLocalizedString("ONBOARDING_PERMISSIONS_GIVE_ACCESS_BUTTON", + comment: "Label for the 'give access' button in the 'onboarding permissions' view."), + selector: #selector(giveAccessPressed)) + + let notNowButton = self.button(title: NSLocalizedString("ONBOARDING_PERMISSIONS_NOT_NOW_BUTTON", + comment: "Label for the 'not now' button in the 'onboarding permissions' view."), + selector: #selector(notNowPressed)) let buttonStack = UIStackView(arrangedSubviews: [ giveAccessButton, @@ -88,7 +51,7 @@ public class OnboardingPermissionsViewController: OWSViewController { buttonStack.spacing = 12 let stackView = UIStackView(arrangedSubviews: [ - explainerLabel, + explanationLabel, buttonStack ]) stackView.axis = .vertical @@ -146,12 +109,6 @@ public class OnboardingPermissionsViewController: OWSViewController { return promise } - // MARK: Orientation - - public override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return .portrait - } - // MARK: - Events @objc func skipWasPressed() { @@ -160,7 +117,7 @@ public class OnboardingPermissionsViewController: OWSViewController { onboardingController.onboardingPermissionsWasSkipped(viewController: self) } - @objc func explainerLabelTapped(sender: UIGestureRecognizer) { + @objc func explanationLabelTapped(sender: UIGestureRecognizer) { guard sender.state == .recognized else { return } diff --git a/Signal/src/ViewControllers/Registration/OnboardingSplashViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingSplashViewController.swift new file mode 100644 index 000000000..aa4b0ce71 --- /dev/null +++ b/Signal/src/ViewControllers/Registration/OnboardingSplashViewController.swift @@ -0,0 +1,91 @@ +// +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// + +import UIKit +import PromiseKit + +@objc +public class OnboardingSplashViewController: OnboardingBaseViewController { + + override public func loadView() { + super.loadView() + + view.backgroundColor = Theme.backgroundColor + view.layoutMargins = .zero + + // TODO: + // navigationItem.title = NSLocalizedString("SETTINGS_BACKUP", comment: "Label for the backup view in app settings.") + + let heroImage = UIImage(named: "onboarding_splash_hero") + let heroImageView = UIImageView(image: heroImage) + heroImageView.contentMode = .scaleAspectFit + heroImageView.layer.minificationFilter = kCAFilterTrilinear + heroImageView.layer.magnificationFilter = kCAFilterTrilinear + heroImageView.setCompressionResistanceLow() + heroImageView.setContentHuggingVerticalLow() + + let titleLabel = self.titleLabel(text: NSLocalizedString("ONBOARDING_SPLASH_TITLE", comment: "Title of the 'onboarding splash' view.")) + view.addSubview(titleLabel) + titleLabel.autoPinWidthToSuperviewMargins() + titleLabel.autoPinEdge(toSuperviewMargin: .top) + + // TODO: Finalize copy. + let explanationLabel = self.explanationLabel(explanationText: NSLocalizedString("ONBOARDING_SPLASH_EXPLANATION", + comment: "Explanation in the 'onboarding splash' view."), + linkText: NSLocalizedString("ONBOARDING_SPLASH_TERM_AND_PRIVACY_POLICY", + comment: "Link to the 'terms and privacy policy' in the 'onboarding splash' view."), + selector: #selector(explanationLabelTapped)) + + // TODO: Make sure this all fits if dynamic font sizes are maxed out. + let continueButton = self.button(title: NSLocalizedString("BUTTON_CONTINUE", + comment: "Label for 'continue' button."), + selector: #selector(continuePressed)) + view.addSubview(continueButton) + + let stackView = UIStackView(arrangedSubviews: [ + heroImageView, + UIView.spacer(withHeight: 22), + titleLabel, + UIView.spacer(withHeight: 56), + explanationLabel, + UIView.spacer(withHeight: 40), + continueButton + ]) + stackView.axis = .vertical + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: 32, left: 32, bottom: 32, right: 32) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.autoPinWidthToSuperviewMargins() + stackView.autoPin(toTopLayoutGuideOf: self, withInset: 0) + stackView.autoPin(toBottomLayoutGuideOf: self, withInset: 0) + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.navigationController?.isNavigationBarHidden = true + } + + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.navigationController?.isNavigationBarHidden = true + } + + // MARK: - Events + + @objc func explanationLabelTapped(sender: UIGestureRecognizer) { + guard sender.state == .recognized else { + return + } + // TODO: + } + + @objc func continuePressed() { + Logger.info("") + + onboardingController.onboardingSplashDidComplete(viewController: self) + } +} diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index ef6fbda3c..8d153c632 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1496,6 +1496,30 @@ /* No comment provided by engineer. */ "OK" = "OK"; +/* Explanation in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_EXPLANATION" = "ONBOARDING_PERMISSIONS_EXPLANATION"; + +/* Label for the 'give access' button in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_GIVE_ACCESS_BUTTON" = "Give Access"; + +/* Link to the 'learn more' in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_LEARN_MORE_LINK" = "Learn More"; + +/* Label for the 'not now' button in the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_NOT_NOW_BUTTON" = "Not Now"; + +/* Title of the 'onboarding permissions' view. */ +"ONBOARDING_PERMISSIONS_TITLE" = "We need access to your contacts and notifications"; + +/* Explanation in the 'onboarding splash' view. */ +"ONBOARDING_SPLASH_EXPLANATION" = "By continuing, you agree to Signal's terms."; + +/* Link to the 'terms and privacy policy' in the 'onboarding splash' view. */ +"ONBOARDING_SPLASH_TERM_AND_PRIVACY_POLICY" = "Terms and Privacy Policy"; + +/* Title of the 'onboarding splash' view. */ +"ONBOARDING_SPLASH_TITLE" = "Signal is the private messenger for everybody"; + /* Button text which opens the settings app */ "OPEN_SETTINGS_BUTTON" = "Settings"; diff --git a/SignalMessaging/categories/UIView+OWS.swift b/SignalMessaging/categories/UIView+OWS.swift index 1e826eb0a..d6ce22e95 100644 --- a/SignalMessaging/categories/UIView+OWS.swift +++ b/SignalMessaging/categories/UIView+OWS.swift @@ -72,4 +72,18 @@ extension UIView { return nil } } + + @objc + public class func spacer(withWidth width: CGFloat) -> UIView { + let view = UIView() + view.autoSetDimension(.width, toSize: width) + return view + } + + @objc + public class func spacer(withHeight height: CGFloat) -> UIView { + let view = UIView() + view.autoSetDimension(.height, toSize: height) + return view + } }