Commit acc48b63 by Dmitriy Stepanets

Finished WidgetPromotion controller UI and swipe down to close gesture

parent 6c9b7835
No preview for this file type
...@@ -57,6 +57,7 @@ ...@@ -57,6 +57,7 @@
CD3884842657BBCC0070FD6F /* DelayedSaveStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CD3884822657BBCC0070FD6F /* DelayedSaveStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CD3884842657BBCC0070FD6F /* DelayedSaveStorage.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CD3884822657BBCC0070FD6F /* DelayedSaveStorage.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CD39F2F225DE94C4009FE398 /* SunPhaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F125DE94C4009FE398 /* SunPhaseCell.swift */; }; CD39F2F225DE94C4009FE398 /* SunPhaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F125DE94C4009FE398 /* SunPhaseCell.swift */; };
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; }; CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD39F2F425DE9571009FE398 /* ArrowButton.swift */; };
CD3D567F268C705900DB99B6 /* PromotionPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3D567E268C705900DB99B6 /* PromotionPresentationAnimator.swift */; };
CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; }; CD3F6E6925FA59D4002DB99B /* ForecastDetailPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */; };
CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; }; CD3F6E6C25FA5A90002DB99B /* PeriodButtonProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */; };
CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4742CF261200500061AC95 /* TodayAlertCell.swift */; }; CD4742D0261200500061AC95 /* TodayAlertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD4742CF261200500061AC95 /* TodayAlertCell.swift */; };
...@@ -305,6 +306,7 @@ ...@@ -305,6 +306,7 @@
CD3884822657BBCC0070FD6F /* DelayedSaveStorage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DelayedSaveStorage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CD3884822657BBCC0070FD6F /* DelayedSaveStorage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DelayedSaveStorage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CD39F2F125DE94C4009FE398 /* SunPhaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunPhaseCell.swift; sourceTree = "<group>"; }; CD39F2F125DE94C4009FE398 /* SunPhaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SunPhaseCell.swift; sourceTree = "<group>"; };
CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; }; CD39F2F425DE9571009FE398 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = "<group>"; };
CD3D567E268C705900DB99B6 /* PromotionPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromotionPresentationAnimator.swift; sourceTree = "<group>"; };
CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDetailPeriodButton.swift; sourceTree = "<group>"; }; CD3F6E6825FA59D4002DB99B /* ForecastDetailPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastDetailPeriodButton.swift; sourceTree = "<group>"; };
CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodButtonProtocol.swift; sourceTree = "<group>"; }; CD3F6E6B25FA5A90002DB99B /* PeriodButtonProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodButtonProtocol.swift; sourceTree = "<group>"; };
CD4742CF261200500061AC95 /* TodayAlertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayAlertCell.swift; sourceTree = "<group>"; }; CD4742CF261200500061AC95 /* TodayAlertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayAlertCell.swift; sourceTree = "<group>"; };
...@@ -666,6 +668,14 @@ ...@@ -666,6 +668,14 @@
path = Cells; path = Cells;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CD3D567D268C703700DB99B6 /* Animators */ = {
isa = PBXGroup;
children = (
CD3D567E268C705900DB99B6 /* PromotionPresentationAnimator.swift */,
);
path = Animators;
sourceTree = "<group>";
};
CD5692B22653D46100A3CDBE /* SplashAnimation */ = { CD5692B22653D46100A3CDBE /* SplashAnimation */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -815,6 +825,7 @@ ...@@ -815,6 +825,7 @@
CD857EA0268B290500B5E069 /* WidgetPromotion */ = { CD857EA0268B290500B5E069 /* WidgetPromotion */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CD3D567D268C703700DB99B6 /* Animators */,
CD857EA3268B36D000B5E069 /* Views */, CD857EA3268B36D000B5E069 /* Views */,
CD857EA1268B290500B5E069 /* WidgetPromotionController.swift */, CD857EA1268B290500B5E069 /* WidgetPromotionController.swift */,
); );
...@@ -1537,6 +1548,7 @@ ...@@ -1537,6 +1548,7 @@
CD32CE0E260C770E00235081 /* MenuHeaderView.swift in Sources */, CD32CE0E260C770E00235081 /* MenuHeaderView.swift in Sources */,
CD15DB3D25DA6C5100024727 /* ForecastTimePeriodControl.swift in Sources */, CD15DB3D25DA6C5100024727 /* ForecastTimePeriodControl.swift in Sources */,
CD67617726259DD70079D273 /* MapLayersPresentationAnimator.swift in Sources */, CD67617726259DD70079D273 /* MapLayersPresentationAnimator.swift in Sources */,
CD3D567F268C705900DB99B6 /* PromotionPresentationAnimator.swift in Sources */,
CD7BF1582620410800A30DF5 /* RadarCoordinator.swift in Sources */, CD7BF1582620410800A30DF5 /* RadarCoordinator.swift in Sources */,
CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */, CDD0F1EE25725BCF00CF5017 /* ThemeManager.swift in Sources */,
CD1237F4255D889F00C98139 /* GradientView.swift in Sources */, CD1237F4255D889F00C98139 /* GradientView.swift in Sources */,
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key> <key>OneWeatherNotificationServiceExtension.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>
<integer>57</integer> <integer>58</integer>
</dict> </dict>
<key>PG (Playground) 1.xcscheme</key> <key>PG (Playground) 1.xcscheme</key>
<dict> <dict>
......
...@@ -420,6 +420,8 @@ ...@@ -420,6 +420,8 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>GADNativeAdValidatorEnabled</key>
<false/>
<key>NSUserTrackingUsageDescription</key> <key>NSUserTrackingUsageDescription</key>
<string>This will be used to deliver personalized ads to you.</string> <string>This will be used to deliver personalized ads to you.</string>
</dict> </dict>
......
{
"images" : [
{
"filename" : "round_close.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2B",
"green" : "0x26",
"red" : "0x26"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFF"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x2B",
"green" : "0x26",
"red" : "0x26"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
...@@ -282,3 +282,4 @@ ...@@ -282,3 +282,4 @@
"widget.promotion.small" = "Small"; "widget.promotion.small" = "Small";
"widget.promotion.medium" = "Medium"; "widget.promotion.medium" = "Medium";
"widget.promotion.large" = "Large"; "widget.promotion.large" = "Large";
"widget.promotion.learn" = "Learn to add widget";
...@@ -120,4 +120,13 @@ struct DefaultTheme: ThemeProtocol { ...@@ -120,4 +120,13 @@ struct DefaultTheme: ThemeProtocol {
var nativeAdCallToActionColor: UIColor { var nativeAdCallToActionColor: UIColor {
return UIColor(named: "native_ad_call_to_action_background") ?? .red return UIColor(named: "native_ad_call_to_action_background") ?? .red
} }
//Widget promotion
var widgetPromotionBackground: UIColor {
return UIColor(named: "widget_promotion_background") ?? .red
}
var widgetPromotionText: UIColor {
return UIColor(named: "widget_promotion_text") ?? .red
}
} }
...@@ -9,47 +9,51 @@ import UIKit ...@@ -9,47 +9,51 @@ import UIKit
public protocol ThemeProtocol { public protocol ThemeProtocol {
//Base //Base
var name:String { get } var name: String { get }
var baseBackgroundColor:UIColor { get } var baseBackgroundColor: UIColor { get }
var containerBackgroundColor:UIColor { get } var containerBackgroundColor: UIColor { get }
//Navigation bar //Navigation bar
var navigationBarBackgroundColor:UIColor { get } var navigationBarBackgroundColor: UIColor { get }
var navigationTintColor:UIColor { get } var navigationTintColor: UIColor { get }
//Tab bar //Tab bar
var tabBarBackgroundColor:UIColor { get } var tabBarBackgroundColor: UIColor { get }
var tabBarTintColor:UIColor { get } var tabBarTintColor: UIColor { get }
var tabBarNormalColor:UIColor { get } var tabBarNormalColor: UIColor { get }
//Text //Text
var primaryTextColor:UIColor { get } var primaryTextColor: UIColor { get }
var secondaryTextColor:UIColor { get } var secondaryTextColor: UIColor { get }
//Progress indicator //Progress indicator
var progressBackgroundColor:UIColor { get } var progressBackgroundColor: UIColor { get }
var progressIndicatorColor:UIColor { get } var progressIndicatorColor: UIColor { get }
//Period button //Period button
var periodButtonBackgroundColor:UIColor { get } var periodButtonBackgroundColor: UIColor { get }
var periodButtonSelectedBackgroundColor:UIColor { get } var periodButtonSelectedBackgroundColor: UIColor { get }
var periodButtonBorderColor:UIColor { get } var periodButtonBorderColor: UIColor { get }
var periodButtonShadowColor:UIColor { get } var periodButtonShadowColor: UIColor { get }
//Segment control //Segment control
var segmentTextColor:UIColor { get } var segmentTextColor: UIColor { get }
var segmentSelectedTextColor:UIColor { get } var segmentSelectedTextColor: UIColor { get }
var segmentBackgroundColor:UIColor { get } var segmentBackgroundColor: UIColor { get }
var segmentBorderColor:UIColor { get } var segmentBorderColor: UIColor { get }
var segmentSelectedGradient:[UIColor] { get } var segmentSelectedGradient: [UIColor] { get }
//Graph //Graph
var graphColor:UIColor { get } var graphColor: UIColor { get }
var graphTintColor:UIColor { get } var graphTintColor: UIColor { get }
//Map //Map
var mapControlsColor:UIColor { get } var mapControlsColor: UIColor { get }
//Ads //Ads
var nativeAdCallToActionColor: UIColor { get } var nativeAdCallToActionColor: UIColor { get }
//Widget promotion
var widgetPromotionBackground: UIColor { get }
var widgetPromotionText: UIColor { get }
} }
...@@ -65,7 +65,11 @@ class TodayViewController: UIViewController { ...@@ -65,7 +65,11 @@ class TodayViewController: UIViewController {
} }
@objc private func handleNotificationButton() { @objc private func handleNotificationButton() {
self.coordinator.openNotificationsScreen() if #available(iOS 14, *) {
self.present(WidgetPromotionController(), animated: true)
}
// self.coordinator.openNotificationsScreen()
} }
} }
......
//
// PromotionDismissAnimator.swift
// 1Weather
//
// Created by Dmitry Stepanets on 30.06.2021.
//
import UIKit
class PromotionDismissAnimator: NSObject, UIViewControllerAnimatedTransitioning {
private let kDuration = 0.25
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return kDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard
let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to)
else {
return
}
// toVC is the Main View Controller
transitionContext.containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
// fromVC is the Modal VC.
// Hide it for now, since we're going to use snapshots instead.
fromVC.view.isHidden = true
// Create the snapshot.
if let snapshot = fromVC.view.snapshotView(afterScreenUpdates: false) {
// Don't forget to add it
transitionContext.containerView.insertSubview(snapshot, aboveSubview: toVC.view)
UIView.animate(withDuration: kDuration) {
snapshot.center.y += UIScreen.main.bounds.height
} completion: { _ in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
}
//
// PromotionPresentationAnimator.swift
// 1Weather
//
// Created by Dmitry Stepanets on 30.06.2021.
//
import UIKit
@available(iOS 14, *)
class PromotionPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.25
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to) as? WidgetPromotionController else {
return
}
let container = transitionContext.containerView
let contentHeight = toViewController.controllerContentHeight
toViewController.view.frame = .init(x: 0,
y: container.bounds.height,
width: container.frame.width,
height: contentHeight)
container.addSubview(toViewController.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
toViewController.view.frame.origin.y = container.frame.height - contentHeight
} completion: { finished in
transitionContext.completeTransition(finished)
}
}
}
//
// WidgetPromotionInteractor.swift
// 1Weather
//
// Created by Dmitry Stepanets on 30.06.2021.
//
import UIKit
class WidgetPromotionInteractor: UIPercentDrivenInteractiveTransition {
var hasStarted = false
var shouldFinish = false
}
...@@ -37,16 +37,9 @@ class PromotionHeaderView: UIView { ...@@ -37,16 +37,9 @@ class PromotionHeaderView: UIView {
} }
private func updateUI() { private func updateUI() {
switch interfaceStyle { plusImageView.tintColor = ThemeManager.currentTheme.widgetPromotionText
case .light: widgetLabel.textColor = ThemeManager.currentTheme.widgetPromotionText
plusImageView.tintColor = UIColor(hex: 0x26262b) descriptionLabel.textColor = ThemeManager.currentTheme.widgetPromotionText
widgetLabel.textColor = UIColor(hex: 0x26262b)
descriptionLabel.textColor = UIColor(hex: 0x26262b)
case .dark:
plusImageView.tintColor = .white
widgetLabel.textColor = .white
descriptionLabel.textColor = .white
}
} }
} }
......
...@@ -21,9 +21,9 @@ class PromotionSmallWidgetView: UIView { ...@@ -21,9 +21,9 @@ class PromotionSmallWidgetView: UIView {
preparePlaceholder() preparePlaceholder()
prepareTopLabel() prepareTopLabel()
prepareHorizontalLine() prepareHorizontalLine()
prepareSizeNumberLabel()
prepareVerticalLine()
prepareSizesLabel() prepareSizesLabel()
prepareVerticalLine()
prepareSizeNumberLabel()
updateUI() updateUI()
} }
...@@ -37,19 +37,17 @@ class PromotionSmallWidgetView: UIView { ...@@ -37,19 +37,17 @@ class PromotionSmallWidgetView: UIView {
} }
private func updateUI() { private func updateUI() {
topLabel.textColor = ThemeManager.currentTheme.widgetPromotionText
sizeNumberLabel.textColor = ThemeManager.currentTheme.widgetPromotionText
sizesLabel.textColor = ThemeManager.currentTheme.widgetPromotionText
switch interfaceStyle { switch interfaceStyle {
case .light: case .light:
topLabel.textColor = UIColor(hex: 0x26262b)
sizeNumberLabel.textColor = UIColor(hex: 0x26262b)
sizesLabel.textColor = UIColor(hex: 0x26262b)
horizontalLineView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
verticalLineView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
case .dark:
topLabel.textColor = .white
sizeNumberLabel.textColor = .white
sizesLabel.textColor = .white
horizontalLineView.backgroundColor = UIColor(hex: 0xffffff).withAlphaComponent(0.1) horizontalLineView.backgroundColor = UIColor(hex: 0xffffff).withAlphaComponent(0.1)
verticalLineView.backgroundColor = UIColor(hex: 0xffffff).withAlphaComponent(0.1) verticalLineView.backgroundColor = UIColor(hex: 0xffffff).withAlphaComponent(0.1)
case .dark:
horizontalLineView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
verticalLineView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
} }
} }
} }
...@@ -111,7 +109,7 @@ private extension PromotionSmallWidgetView { ...@@ -111,7 +109,7 @@ private extension PromotionSmallWidgetView {
addSubview(sizeNumberLabel) addSubview(sizeNumberLabel)
sizeNumberLabel.snp.makeConstraints { make in sizeNumberLabel.snp.makeConstraints { make in
make.left.equalTo(widgetPlaceholder.snp.right).offset(33) make.right.equalTo(verticalLineView.snp.left).offset(-20)
make.top.equalTo(horizontalLineView.snp.bottom).offset(17) make.top.equalTo(horizontalLineView.snp.bottom).offset(17)
} }
} }
...@@ -123,8 +121,8 @@ private extension PromotionSmallWidgetView { ...@@ -123,8 +121,8 @@ private extension PromotionSmallWidgetView {
verticalLineView.snp.makeConstraints { make in verticalLineView.snp.makeConstraints { make in
make.width.equalTo(1) make.width.equalTo(1)
make.top.equalTo(horizontalLineView.snp.bottom).offset(10) make.top.equalTo(horizontalLineView.snp.bottom).offset(10)
make.height.equalTo(70) make.height.equalTo(sizesLabel)
make.centerX.equalTo(horizontalLineView) make.right.equalTo(sizesLabel.snp.left).offset(-13)
} }
} }
...@@ -133,13 +131,15 @@ private extension PromotionSmallWidgetView { ...@@ -133,13 +131,15 @@ private extension PromotionSmallWidgetView {
let medium = "widget.promotion.medium".localized() let medium = "widget.promotion.medium".localized()
let large = "widget.promotion.large".localized() let large = "widget.promotion.large".localized()
let paragraph = NSMutableParagraphStyle()
paragraph.lineSpacing = 4
let attrString = NSMutableAttributedString(string: small + "\n" + medium + "\n" + large) let attrString = NSMutableAttributedString(string: small + "\n" + medium + "\n" + large)
attrString.addAttribute(.font, attrString.addAttributes([.font : UIFont.systemFont(ofSize: 14, weight: .medium),
value: UIFont.systemFont(ofSize: 14, weight: .medium), .paragraphStyle : paragraph],
range: .init(location: 0, length: small.count)) range: .init(location: 0, length: small.count))
attrString.addAttribute(.font, attrString.addAttributes([.font : UIFont.systemFont(ofSize: 20, weight: .semibold)],
value: UIFont.systemFont(ofSize: 20, weight: .semibold), range: .init(location: small.count, length: medium.count + 1))
range: .init(location: small.count, length: medium.count + 1))
attrString.addAttribute(.font, attrString.addAttribute(.font,
value: UIFont.systemFont(ofSize: 28, weight: .bold), value: UIFont.systemFont(ofSize: 28, weight: .bold),
range: .init(location: small.count + medium.count + 1, length: large.count + 1)) range: .init(location: small.count + medium.count + 1, length: large.count + 1))
...@@ -151,9 +151,8 @@ private extension PromotionSmallWidgetView { ...@@ -151,9 +151,8 @@ private extension PromotionSmallWidgetView {
addSubview(sizesLabel) addSubview(sizesLabel)
sizesLabel.snp.makeConstraints { make in sizesLabel.snp.makeConstraints { make in
make.left.equalTo(verticalLineView.snp.right).offset(13) make.top.equalTo(horizontalLineView.snp.bottom).offset(8)
make.top.equalTo(verticalLineView) make.right.equalTo(horizontalLineView)
make.right.equalToSuperview()
} }
} }
} }
...@@ -10,17 +10,45 @@ import UIKit ...@@ -10,17 +10,45 @@ import UIKit
@available(iOS 14, *) @available(iOS 14, *)
class WidgetPromotionController: UIViewController { class WidgetPromotionController: UIViewController {
//Private //Private
private let kPanTopInset: CGFloat = 10
private let kScrollViewTopInset: CGFloat = 36
private let controllerPanView = UIView() private let controllerPanView = UIView()
private let closeButton = UIButton() private let closeButton = UIButton()
private let scrollView = UIScrollView() private let scrollView = UIScrollView()
private let stackView = UIStackView() private let stackView = UIStackView()
private let footerView = UIView()
private let learnButton = UIButton()
private var initialTouchPoint = CGPoint(x: 0,y: 0)
private lazy var panGesture: UIPanGestureRecognizer = {
let gesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(sender:)))
return gesture
}()
//Public
var controllerContentHeight: CGFloat {
let scrollViewBottomInset: CGFloat = 4
let pan = controllerPanView.frame.height + kPanTopInset
let stackHeight = stackView.frame.height
let footerHeight = footerView.frame.height
let safeAreaInset = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
return pan + kScrollViewTopInset + stackHeight + scrollViewBottomInset + footerHeight + safeAreaInset
}
init() { init() {
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .custom
transitioningDelegate = self
}
override func viewDidLoad() {
super.viewDidLoad()
prepareView() prepareView()
preparePanView() preparePanView()
prepareCloseButton() prepareCloseButton()
prepareFooterView()
prepareScrollView() prepareScrollView()
prepareStackView() prepareStackView()
updateUI() updateUI()
...@@ -34,8 +62,45 @@ class WidgetPromotionController: UIViewController { ...@@ -34,8 +62,45 @@ class WidgetPromotionController: UIViewController {
self.dismiss(animated: true) self.dismiss(animated: true)
} }
@objc private func handleLearnButton() {
}
@objc private func handlePanGesture(sender: UIPanGestureRecognizer) {
let touchPoint = sender.location(in: self.view?.window)
let originalOffsetY = UIScreen.main.bounds.height - self.view.bounds.height
switch sender.state {
case .began:
initialTouchPoint = touchPoint
case .changed:
if touchPoint.y - initialTouchPoint.y > 0 {
view.frame.origin.y = originalOffsetY + (touchPoint.y - initialTouchPoint.y)
}
case .cancelled, .ended:
if touchPoint.y - initialTouchPoint.y > 80 {
self.dismiss(animated: true, completion: nil)
} else {
UIView.animate(withDuration: 0.25, animations: {
self.view.frame.origin.y = originalOffsetY
})
}
break
default:
break
}
}
private func updateUI() { private func updateUI() {
view.backgroundColor = ThemeManager.currentTheme.baseBackgroundColor view.backgroundColor = ThemeManager.currentTheme.widgetPromotionBackground
footerView.backgroundColor = ThemeManager.currentTheme.widgetPromotionBackground
switch view.interfaceStyle {
case .light:
footerView.layer.shadowColor = UIColor(hex: 0x4c4c4c).cgColor
case .dark:
footerView.layer.shadowColor = UIColor(hex: 0xAAAAAA).cgColor
}
} }
} }
...@@ -43,9 +108,16 @@ class WidgetPromotionController: UIViewController { ...@@ -43,9 +108,16 @@ class WidgetPromotionController: UIViewController {
@available(iOS 14, *) @available(iOS 14, *)
private extension WidgetPromotionController { private extension WidgetPromotionController {
func prepareView() { func prepareView() {
view.addGestureRecognizer(panGesture)
view.clipsToBounds = true view.clipsToBounds = true
view.layer.cornerRadius = 24 view.layer.cornerRadius = 24
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.5
view.layer.shadowOffset = .init(width: 0, height: 0)
view.layer.shadowRadius = 18
} }
func preparePanView() { func preparePanView() {
...@@ -56,14 +128,14 @@ private extension WidgetPromotionController { ...@@ -56,14 +128,14 @@ private extension WidgetPromotionController {
controllerPanView.snp.makeConstraints { make in controllerPanView.snp.makeConstraints { make in
make.width.equalTo(30) make.width.equalTo(30)
make.height.equalTo(4) make.height.equalTo(4)
make.top.equalToSuperview().inset(10) make.top.equalToSuperview().inset(kPanTopInset)
make.centerX.equalToSuperview() make.centerX.equalToSuperview()
} }
} }
func prepareCloseButton() { func prepareCloseButton() {
closeButton.addTarget(self, action: #selector(handleCloseButton), for: .touchUpInside) closeButton.addTarget(self, action: #selector(handleCloseButton), for: .touchUpInside)
closeButton.setImage(UIImage(systemName: "xmark.circle.fill"), for: .normal) closeButton.setImage(UIImage(named: "round_close"), for: .normal)
closeButton.imageView?.contentMode = .scaleAspectFit closeButton.imageView?.contentMode = .scaleAspectFit
closeButton.tintColor = UIColor(hex: 0xe4e4e4).withAlphaComponent(0.4) closeButton.tintColor = UIColor(hex: 0xe4e4e4).withAlphaComponent(0.4)
view.addSubview(closeButton) view.addSubview(closeButton)
...@@ -78,8 +150,9 @@ private extension WidgetPromotionController { ...@@ -78,8 +150,9 @@ private extension WidgetPromotionController {
func prepareScrollView() { func prepareScrollView() {
view.addSubview(scrollView) view.addSubview(scrollView)
scrollView.snp.makeConstraints { make in scrollView.snp.makeConstraints { make in
make.left.bottom.right.equalToSuperview() make.left.right.equalToSuperview()
make.top.equalToSuperview().inset(36) make.top.equalToSuperview().inset(kScrollViewTopInset)
make.bottom.equalTo(footerView.snp.top).offset(4)
} }
} }
...@@ -94,5 +167,48 @@ private extension WidgetPromotionController { ...@@ -94,5 +167,48 @@ private extension WidgetPromotionController {
stackView.snp.makeConstraints { make in stackView.snp.makeConstraints { make in
make.edges.width.equalToSuperview() make.edges.width.equalToSuperview()
} }
stackView.layoutIfNeeded()
}
func prepareFooterView() {
footerView.layer.shadowOpacity = 0.5
footerView.layer.shadowRadius = 7
footerView.layer.shadowOffset = .init(width: 0, height: 0)
view.addSubview(footerView)
learnButton.addTarget(self, action: #selector(handleLearnButton), for: .touchUpInside)
learnButton.setTitle("widget.promotion.learn".localized(), for: .normal)
learnButton.setTitleColor(ThemeManager.currentTheme.graphTintColor, for: .normal)
learnButton.setTitleColor(ThemeManager.currentTheme.graphTintColor.darken(by: 10), for: .highlighted)
learnButton.layer.borderWidth = 2
learnButton.layer.borderColor = ThemeManager.currentTheme.graphTintColor.cgColor
learnButton.layer.cornerRadius = 6
footerView.addSubview(learnButton)
//Constraints
footerView.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview()
}
learnButton.snp.makeConstraints { make in
make.left.right.equalToSuperview().inset(20)
make.top.equalToSuperview().inset(10)
let safeAreaInset = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0
make.bottom.equalToSuperview().inset(safeAreaInset + 10)
}
footerView.layoutIfNeeded()
}
}
//MARK:- Transitioning Delegate
@available(iOS 14, *)
extension WidgetPromotionController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PromotionPresentationAnimator()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return nil
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment