-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix: Fix UI glitch after toast is dismissed #1600 (#1816) Increase min screen on time for toast to 1.5 seconds from 1 second. * Add battery level check on FW upgrade from Discover (#1819) Implements battery charge check on Firmware upgrade process on Discover * task: Disable RSSI alert editing for local sensors #1751 (#1820) * fix: View menu not updating after sync from cloud #1632 (#1822) * fix: Improve activity presenter transition #1600 (#1821) --------- Co-authored-by: Rinat Enikeev <rinat.enikeev@gmail.com>
- Loading branch information
1 parent
567c245
commit 58f145e
Showing
16 changed files
with
290 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 121 additions & 29 deletions
150
...uviPresenters/Sources/RuuviPresenters/Activity/RuuviLogo/ActivityPresenterRuuviLogo.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,146 @@ | ||
import UIKit | ||
|
||
public final class ActivityPresenterRuuviLogo: ActivityPresenter { | ||
let minAnimationTime: CFTimeInterval = 1.5 | ||
var startTime: CFTimeInterval? | ||
let window = UIWindow(frame: UIScreen.main.bounds) | ||
let activityPresenterViewProvider: ActivityPresenterViewProvider | ||
let activityPresenterViewController: UIViewController | ||
let stateHolder = ActivityPresenterStateHolder() | ||
public final class ActivityPresenterRuuviLogo { | ||
// Minimum duration to keep the activity indicator on the screen | ||
private let minAnimationTime: CFTimeInterval = 1.5 | ||
// Animation duration for presenting and dismissing the activity indicator. | ||
private let animationDuration: CGFloat = 0.5 | ||
// Vertical(top/bottom) padding for activity indicator depending on position. | ||
private let verticalPadding: CGFloat = 8 | ||
|
||
weak var appWindow: UIWindow? | ||
private let activityPresenterViewProvider: ActivityPresenterViewProvider | ||
private let activityPresenterViewController: UIViewController | ||
private let stateHolder = ActivityPresenterStateHolder() | ||
|
||
private var activityPresenterPosition: ActivityPresenterPosition = .bottom | ||
private var startTime: CFTimeInterval? | ||
|
||
public init() { | ||
activityPresenterViewProvider = ActivityPresenterViewProvider(stateHolder: stateHolder) | ||
activityPresenterViewController = activityPresenterViewProvider.makeViewController() | ||
activityPresenterViewController.view.backgroundColor = .clear | ||
window.windowLevel = .normal | ||
activityPresenterViewController.view.translatesAutoresizingMaskIntoConstraints = false | ||
window.rootViewController = activityPresenterViewController | ||
} | ||
} | ||
|
||
public extension ActivityPresenterRuuviLogo { | ||
func setPosition(_ position: ActivityPresenterPosition) { | ||
extension ActivityPresenterRuuviLogo: ActivityPresenter { | ||
public func show( | ||
with state: ActivityPresenterState, | ||
atPosition position: ActivityPresenterPosition | ||
) { | ||
activityPresenterPosition = position | ||
activityPresenterViewProvider.updatePosition(position) | ||
} | ||
|
||
func show(with state: ActivityPresenterState) { | ||
startTime = CFAbsoluteTimeGetCurrent() | ||
appWindow = UIWindow.key | ||
window.makeKeyAndVisible() | ||
window.layoutIfNeeded() | ||
|
||
guard let topController = RuuviPresenterHelper.topViewController(), | ||
let toastView = activityPresenterViewController.view else { | ||
return | ||
} | ||
|
||
topController.view.addSubview(toastView) | ||
setupConstraints(for: toastView, in: topController) | ||
animateActivityViewPresentation(toastView, atPosition: position) | ||
|
||
activityPresenterViewProvider.updateState(state) | ||
} | ||
|
||
func update(with state: ActivityPresenterState) { | ||
// Reset start time with state update since we want to show | ||
// latest state message before dismissing the presenter. | ||
public func update(with state: ActivityPresenterState) { | ||
startTime = CFAbsoluteTimeGetCurrent() | ||
activityPresenterViewProvider.updateState(state) | ||
} | ||
|
||
func dismiss(immediately: Bool) { | ||
public func dismiss(immediately: Bool) { | ||
let executionTime = CFAbsoluteTimeGetCurrent() - (startTime ?? 0) | ||
let additionalWaitTime = immediately ? 0 : | ||
executionTime < minAnimationTime ? (minAnimationTime - executionTime) : 0 | ||
DispatchQueue.main.asyncAfter(deadline: .now() + additionalWaitTime) { [weak self] in | ||
self?.window.isHidden = true | ||
self?.appWindow?.makeKeyAndVisible() | ||
self?.appWindow = nil | ||
let additionalWaitTime = immediately ? 0 : max(minAnimationTime - executionTime, 0) | ||
|
||
guard let toastView = activityPresenterViewController.view else { | ||
return | ||
} | ||
|
||
DispatchQueue.main.asyncAfter( | ||
deadline: .now() + additionalWaitTime | ||
) { [weak self] in | ||
self?.animateActivityViewDismissal(toastView) | ||
} | ||
} | ||
} | ||
|
||
extension ActivityPresenterRuuviLogo { | ||
private func setupConstraints( | ||
for view: UIView, | ||
in viewController: UIViewController | ||
) { | ||
view.translatesAutoresizingMaskIntoConstraints = false | ||
NSLayoutConstraint.activate([ | ||
view.leadingAnchor.constraint( | ||
equalTo: viewController.view.leadingAnchor | ||
), | ||
view.trailingAnchor.constraint( | ||
equalTo: viewController.view.trailingAnchor | ||
), | ||
view.topAnchor.constraint( | ||
equalTo: viewController.view.topAnchor | ||
), | ||
view.bottomAnchor.constraint( | ||
equalTo: viewController.view.bottomAnchor | ||
), | ||
]) | ||
} | ||
|
||
private func animateActivityViewPresentation( | ||
_ view: UIView, | ||
atPosition position: ActivityPresenterPosition | ||
) { | ||
UIView.animate( | ||
withDuration: animationDuration, | ||
delay: 0, | ||
options: [.curveEaseOut] | ||
) { [weak self] in | ||
guard let self = self else { return } | ||
switch position { | ||
case .top: | ||
view.transform = CGAffineTransform( | ||
translationX: 0, | ||
y: self.safeAreaInsets().top + self.verticalPadding | ||
) | ||
case .bottom: | ||
view.transform = CGAffineTransform( | ||
translationX: 0, | ||
y: -(self.safeAreaInsets().bottom + self.verticalPadding) | ||
) | ||
case .center: | ||
view.transform = .identity | ||
} | ||
} | ||
} | ||
|
||
private func animateActivityViewDismissal(_ view: UIView) { | ||
UIView.animate( | ||
withDuration: animationDuration, | ||
delay: 0, | ||
options: [.curveEaseIn] | ||
) { [weak self] in | ||
guard let self = self else { return } | ||
switch self.activityPresenterPosition { | ||
case .top: | ||
view.transform = CGAffineTransform( | ||
translationX: 0, | ||
y: -(view.frame.height + verticalPadding) | ||
) | ||
case .bottom: | ||
view.transform = CGAffineTransform( | ||
translationX: 0, | ||
y: view.frame.height + verticalPadding | ||
) | ||
case .center: | ||
break | ||
} | ||
} completion: { [weak self] _ in | ||
view.removeFromSuperview() | ||
self?.activityPresenterViewProvider.updateState(.dismiss) | ||
} | ||
} | ||
|
||
private func safeAreaInsets() -> UIEdgeInsets { | ||
RuuviPresenterHelper.topViewController()?.view.safeAreaInsets ?? .zero | ||
} | ||
} |
13 changes: 11 additions & 2 deletions
13
Common/RuuviPresenters/Sources/RuuviPresenters/ActivityPresenter.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
Common/RuuviPresenters/Sources/RuuviPresenters/Util/RuuviPresenterHelper.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import UIKit | ||
|
||
struct RuuviPresenterHelper { | ||
public static func topViewController() -> UIViewController? { | ||
if var topController = keyWindow()?.rootViewController { | ||
while let presentedViewController = topController.presentedViewController { | ||
topController = presentedViewController | ||
} | ||
return topController | ||
} | ||
return nil | ||
} | ||
|
||
private static func keyWindow() -> UIWindow? { | ||
return UIApplication.shared.windows.first(where: { $0.isKeyWindow }) | ||
} | ||
} |
20 changes: 0 additions & 20 deletions
20
Common/RuuviPresenters/Sources/RuuviPresenters/Util/UIApplication+ViewController.swift
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.