mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-19 13:03:36 +00:00
[ios] refactor Toast class and improve toast message style
1. update style: bigger fonts and insets 2. update background blur 3. get rid of MWM prefix 4. replace the timer with the simplier dispatch async after. In this case there is no needed to create a timer for each toasts message just to add a timeout 5. reorder Toast class methods 6. replace the instance `show` method with a `static show`. Because there non needed to call show every time. We do not have stored toast that will be showed in different places thane created. Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com> Signed-off-by: Kiryl Kaveryn <kirylkaveryn@gmail.com>
This commit is contained in:
committed by
Konstantin Pastbin
parent
72cc4bbd50
commit
103d660603
@@ -1,101 +1,112 @@
|
|||||||
@objc(MWMToast)
|
@objc
|
||||||
final class Toast: NSObject {
|
final class Toast: NSObject {
|
||||||
@objc(MWMToastAlignment)
|
@objc
|
||||||
enum Alignment: Int {
|
enum Alignment: Int {
|
||||||
case bottom
|
case bottom
|
||||||
case top
|
case top
|
||||||
}
|
}
|
||||||
|
|
||||||
private var blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
private enum Constants {
|
||||||
private var timer: Timer?
|
static let presentationDuration: TimeInterval = 3
|
||||||
|
static let animationDuration: TimeInterval = kDefaultAnimationDuration
|
||||||
|
static let bottomOffset: CGFloat = 63
|
||||||
|
static let topOffset: CGFloat = 50
|
||||||
|
static let horizontalOffset: CGFloat = 16
|
||||||
|
static let labelOffsets = UIEdgeInsets(top: 10, left: 14, bottom: -10, right: -14)
|
||||||
|
static let maxWidth: CGFloat = 400
|
||||||
|
}
|
||||||
|
|
||||||
private static var toasts: [Toast] = []
|
private static var toasts: [Toast] = []
|
||||||
|
private var blurView: UIVisualEffectView
|
||||||
@objc static func toast(withText text: String) -> Toast {
|
|
||||||
let toast = Toast(text)
|
|
||||||
toasts.append(toast)
|
|
||||||
return toast
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc static func hideAll() {
|
|
||||||
toasts.forEach { $0.hide() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private init(_ text: String) {
|
private init(_ text: String) {
|
||||||
blurView.layer.setCornerRadius(.buttonDefault)
|
blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
||||||
blurView.clipsToBounds = true
|
blurView.setStyle(.toastBackground)
|
||||||
|
blurView.isUserInteractionEnabled = false
|
||||||
blurView.alpha = 0
|
blurView.alpha = 0
|
||||||
|
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let label = UILabel()
|
let label = UILabel()
|
||||||
label.text = text
|
label.text = text
|
||||||
label.textAlignment = .center
|
label.setStyle(.toastLabel)
|
||||||
label.numberOfLines = 0
|
label.numberOfLines = 0
|
||||||
label.font = .regular14()
|
|
||||||
label.textColor = .white
|
|
||||||
label.translatesAutoresizingMaskIntoConstraints = false
|
label.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
label.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||||
blurView.contentView.addSubview(label)
|
blurView.contentView.addSubview(label)
|
||||||
blurView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
label.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: 8),
|
label.leadingAnchor.constraint(equalTo: blurView.contentView.leadingAnchor, constant: Constants.labelOffsets.left),
|
||||||
label.trailingAnchor.constraint(equalTo: blurView.contentView.trailingAnchor, constant: -8),
|
label.trailingAnchor.constraint(equalTo: blurView.contentView.trailingAnchor, constant: Constants.labelOffsets.right),
|
||||||
label.topAnchor.constraint(equalTo: blurView.contentView.topAnchor, constant: 8),
|
label.topAnchor.constraint(equalTo: blurView.contentView.topAnchor, constant: Constants.labelOffsets.top),
|
||||||
label.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: -8)
|
label.bottomAnchor.constraint(equalTo: blurView.contentView.bottomAnchor, constant: Constants.labelOffsets.bottom)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
// MARK: - Public methods
|
||||||
timer?.invalidate()
|
|
||||||
|
@objc
|
||||||
|
static func show(withText text: String) {
|
||||||
|
show(withText: text, alignment: .bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func show() {
|
@objc
|
||||||
show(in: UIApplication.shared.keyWindow, alignment: .bottom)
|
static func show(withText text: String, alignment: Alignment) {
|
||||||
|
show(withText: text, alignment: alignment, pinToSafeArea: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func show(withAlignment alignment: Alignment, pinToSafeArea: Bool = true) {
|
@objc
|
||||||
show(in: UIApplication.shared.keyWindow, alignment: alignment, pinToSafeArea: pinToSafeArea)
|
static func show(withText text: String, alignment: Alignment, pinToSafeArea: Bool) {
|
||||||
|
let toast = Toast(text)
|
||||||
|
toasts.append(toast)
|
||||||
|
toast.show(withAlignment: alignment, pinToSafeArea: pinToSafeArea)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func show(in view: UIView?, alignment: Alignment, pinToSafeArea: Bool = true) {
|
@objc
|
||||||
guard let view = view else { return }
|
static func hideAll() {
|
||||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
toasts.forEach { $0.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private methods
|
||||||
|
|
||||||
|
private func show(withAlignment alignment: Alignment, pinToSafeArea: Bool) {
|
||||||
|
Self.hideAll()
|
||||||
|
guard let view = UIApplication.shared.keyWindow else { return }
|
||||||
view.addSubview(blurView)
|
view.addSubview(blurView)
|
||||||
|
|
||||||
let leadingConstraint = blurView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 16)
|
let leadingConstraint = blurView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: Constants.horizontalOffset)
|
||||||
let trailingConstraint = blurView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -16)
|
let trailingConstraint = blurView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -Constants.horizontalOffset)
|
||||||
|
let maxWidthConstraint = blurView.widthAnchor.constraint(equalToConstant: Constants.maxWidth).withPriority(.defaultLow)
|
||||||
|
|
||||||
|
let verticalConstraint: NSLayoutConstraint
|
||||||
|
switch alignment {
|
||||||
|
case .bottom:
|
||||||
|
verticalConstraint = blurView.bottomAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.bottomAnchor : view.bottomAnchor,
|
||||||
|
constant: -Constants.bottomOffset)
|
||||||
|
case .top:
|
||||||
|
verticalConstraint = blurView.topAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor,
|
||||||
|
constant: Constants.topOffset)
|
||||||
|
}
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
leadingConstraint,
|
leadingConstraint,
|
||||||
trailingConstraint
|
trailingConstraint,
|
||||||
])
|
maxWidthConstraint,
|
||||||
|
verticalConstraint,
|
||||||
let topConstraint: NSLayoutConstraint
|
|
||||||
if alignment == .bottom {
|
|
||||||
topConstraint = blurView.bottomAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.bottomAnchor : view.bottomAnchor, constant: -63)
|
|
||||||
} else {
|
|
||||||
topConstraint = blurView.topAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.topAnchor : view.topAnchor, constant: 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
topConstraint,
|
|
||||||
blurView.centerXAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.centerXAnchor : view.centerXAnchor)
|
blurView.centerXAnchor.constraint(equalTo: pinToSafeArea ? view.safeAreaLayoutGuide.centerXAnchor : view.centerXAnchor)
|
||||||
])
|
])
|
||||||
|
|
||||||
UIView.animate(withDuration: kDefaultAnimationDuration) {
|
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||||
self.blurView.alpha = 1
|
self.blurView.alpha = 1
|
||||||
|
} , completion: { _ in
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + Constants.presentationDuration) {
|
||||||
|
self.hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
timer = Timer.scheduledTimer(timeInterval: 3,
|
private func hide() {
|
||||||
target: self,
|
|
||||||
selector: #selector(hide),
|
|
||||||
userInfo: nil,
|
|
||||||
repeats: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func hide() {
|
|
||||||
timer?.invalidate()
|
|
||||||
if self.blurView.superview != nil {
|
if self.blurView.superview != nil {
|
||||||
UIView.animate(withDuration: kDefaultAnimationDuration,
|
UIView.animate(withDuration: Constants.animationDuration,
|
||||||
animations: { self.blurView.alpha = 0 }) { [self] _ in
|
animations: { self.blurView.alpha = 0 }) { [self] _ in
|
||||||
self.blurView.removeFromSuperview()
|
self.blurView.removeFromSuperview()
|
||||||
Self.toasts.removeAll(where: { $0 === self }) }
|
Self.toasts.removeAll(where: { $0 === self }) }
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ NSString * const kUDDidShowLongTapToShowSideButtonsToast = @"kUDDidShowLongTapTo
|
|||||||
- (void)setHidden:(BOOL)hidden
|
- (void)setHidden:(BOOL)hidden
|
||||||
{
|
{
|
||||||
if (!self.hidden && hidden)
|
if (!self.hidden && hidden)
|
||||||
[[MWMToast toastWithText:L(@"long_tap_toast")] show];
|
[Toast showWithText:L(@"long_tap_toast")];
|
||||||
|
|
||||||
return [self.sideView setHidden:hidden animated:YES];
|
return [self.sideView setHidden:hidden animated:YES];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||||||
|
|
||||||
- (void)viewWillDisappear:(BOOL)animated {
|
- (void)viewWillDisappear:(BOOL)animated {
|
||||||
[super viewWillDisappear:animated];
|
[super viewWillDisappear:animated];
|
||||||
[MWMToast hideAll];
|
[Toast hideAll];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)configLayout {
|
- (void)configLayout {
|
||||||
@@ -115,7 +115,7 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||||||
break;
|
break;
|
||||||
case MWMMapOverlayTrafficStateNoData:
|
case MWMMapOverlayTrafficStateNoData:
|
||||||
btn.imageName = @"btn_traffic_on";
|
btn.imageName = @"btn_traffic_on";
|
||||||
[[MWMToast toastWithText:L(@"traffic_data_unavailable")] show];
|
[Toast showWithText:L(@"traffic_data_unavailable")];
|
||||||
break;
|
break;
|
||||||
case MWMMapOverlayTrafficStateNetworkError:
|
case MWMMapOverlayTrafficStateNetworkError:
|
||||||
[MWMMapOverlayManager setTrafficEnabled:NO];
|
[MWMMapOverlayManager setTrafficEnabled:NO];
|
||||||
@@ -123,11 +123,11 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||||||
break;
|
break;
|
||||||
case MWMMapOverlayTrafficStateExpiredData:
|
case MWMMapOverlayTrafficStateExpiredData:
|
||||||
btn.imageName = @"btn_traffic_outdated";
|
btn.imageName = @"btn_traffic_outdated";
|
||||||
[[MWMToast toastWithText:L(@"traffic_update_maps_text")] show];
|
[Toast showWithText:L(@"traffic_update_maps_text")];
|
||||||
break;
|
break;
|
||||||
case MWMMapOverlayTrafficStateExpiredApp:
|
case MWMMapOverlayTrafficStateExpiredApp:
|
||||||
btn.imageName = @"btn_traffic_outdated";
|
btn.imageName = @"btn_traffic_outdated";
|
||||||
[[MWMToast toastWithText:L(@"traffic_update_app_message")] show];
|
[Toast showWithText:L(@"traffic_update_app_message")];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||||||
break;
|
break;
|
||||||
case MWMMapOverlayIsolinesStateEnabled:
|
case MWMMapOverlayIsolinesStateEnabled:
|
||||||
if (![MWMMapOverlayManager isolinesVisible])
|
if (![MWMMapOverlayManager isolinesVisible])
|
||||||
[[MWMToast toastWithText:L(@"isolines_toast_zooms_1_10")] show];
|
[Toast showWithText:L(@"isolines_toast_zooms_1_10")];
|
||||||
break;
|
break;
|
||||||
case MWMMapOverlayIsolinesStateExpiredData:
|
case MWMMapOverlayIsolinesStateExpiredData:
|
||||||
[MWMAlertViewController.activeAlertController presentInfoAlert:L(@"isolines_activation_error_dialog")];
|
[MWMAlertViewController.activeAlertController presentInfoAlert:L(@"isolines_activation_error_dialog")];
|
||||||
@@ -162,7 +162,7 @@ NSArray<UIImage *> *imagesWithName(NSString *name) {
|
|||||||
} else if ([MWMMapOverlayManager transitEnabled]) {
|
} else if ([MWMMapOverlayManager transitEnabled]) {
|
||||||
btn.imageName = @"btn_subway_on";
|
btn.imageName = @"btn_subway_on";
|
||||||
if ([MWMMapOverlayManager transitState] == MWMMapOverlayTransitStateNoData)
|
if ([MWMMapOverlayManager transitState] == MWMMapOverlayTransitStateNoData)
|
||||||
[[MWMToast toastWithText:L(@"subway_data_unavailable")] show];
|
[Toast showWithText:L(@"subway_data_unavailable")];
|
||||||
} else if ([MWMMapOverlayManager isoLinesEnabled]) {
|
} else if ([MWMMapOverlayManager isoLinesEnabled]) {
|
||||||
btn.imageName = @"btn_isoMap_on";
|
btn.imageName = @"btn_isoMap_on";
|
||||||
[self handleIsolinesState:[MWMMapOverlayManager isolinesState]];
|
[self handleIsolinesState:[MWMMapOverlayManager isolinesState]];
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ enum GlobalStyleSheet: String, CaseIterable {
|
|||||||
case trackRecordingWidgetButton = "TrackRecordingWidgetButton"
|
case trackRecordingWidgetButton = "TrackRecordingWidgetButton"
|
||||||
case blackOpaqueBackground = "BlackOpaqueBackground"
|
case blackOpaqueBackground = "BlackOpaqueBackground"
|
||||||
case blueBackground = "BlueBackground"
|
case blueBackground = "BlueBackground"
|
||||||
case toastBackground = "ToastBackground"
|
|
||||||
case fadeBackground = "FadeBackground"
|
case fadeBackground = "FadeBackground"
|
||||||
case errorBackground = "ErrorBackground"
|
case errorBackground = "ErrorBackground"
|
||||||
case blackStatusBarBackground = "BlackStatusBarBackground"
|
case blackStatusBarBackground = "BlackStatusBarBackground"
|
||||||
@@ -62,6 +61,8 @@ enum GlobalStyleSheet: String, CaseIterable {
|
|||||||
case grabber
|
case grabber
|
||||||
case modalSheetBackground
|
case modalSheetBackground
|
||||||
case modalSheetContent
|
case modalSheetContent
|
||||||
|
case toastBackground
|
||||||
|
case toastLabel
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GlobalStyleSheet: IStyleSheet {
|
extension GlobalStyleSheet: IStyleSheet {
|
||||||
@@ -197,10 +198,6 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||||||
return .add { s in
|
return .add { s in
|
||||||
s.backgroundColor = colors.linkBlue
|
s.backgroundColor = colors.linkBlue
|
||||||
}
|
}
|
||||||
case .toastBackground:
|
|
||||||
return .add { s in
|
|
||||||
s.backgroundColor = colors.toastBackground
|
|
||||||
}
|
|
||||||
case .fadeBackground:
|
case .fadeBackground:
|
||||||
return .add { s in
|
return .add { s in
|
||||||
s.backgroundColor = colors.fadeBackground
|
s.backgroundColor = colors.fadeBackground
|
||||||
@@ -452,6 +449,17 @@ extension GlobalStyleSheet: IStyleSheet {
|
|||||||
s.backgroundColor = colors.clear
|
s.backgroundColor = colors.clear
|
||||||
s.clip = true
|
s.clip = true
|
||||||
}
|
}
|
||||||
|
case .toastBackground:
|
||||||
|
return .add { s in
|
||||||
|
s.cornerRadius = .modalSheet
|
||||||
|
s.clip = true
|
||||||
|
}
|
||||||
|
case .toastLabel:
|
||||||
|
return .add { s in
|
||||||
|
s.font = fonts.regular16
|
||||||
|
s.fontColor = colors.whitePrimaryText
|
||||||
|
s.textAlignment = .center
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ final class TrackRecordingManager: NSObject {
|
|||||||
|
|
||||||
private func stop(completion: (CompletionHandler)? = nil) {
|
private func stop(completion: (CompletionHandler)? = nil) {
|
||||||
guard !trackRecorder.isTrackRecordingEmpty() else {
|
guard !trackRecorder.isTrackRecordingEmpty() else {
|
||||||
Toast.toast(withText: L("track_recording_toast_nothing_to_save")).show()
|
Toast.show(withText: L("track_recording_toast_nothing_to_save"))
|
||||||
stopRecording(.withoutSaving, completion: completion)
|
stopRecording(.withoutSaving, completion: completion)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ void registerCellsForTableView(std::vector<MWMEditorCellID> const & cells, UITab
|
|||||||
|
|
||||||
- (void)showNotesQueuedToast
|
- (void)showNotesQueuedToast
|
||||||
{
|
{
|
||||||
[[MWMToast toastWithText:L(@"editor_edits_sent_message")] show];
|
[Toast showWithText:L(@"editor_edits_sent_message")];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Headers
|
#pragma mark - Headers
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ private extension AboutController {
|
|||||||
UIPasteboard.general.string = content
|
UIPasteboard.general.string = content
|
||||||
let message = String(format: L("copied_to_clipboard"), content)
|
let message = String(format: L("copied_to_clipboard"), content)
|
||||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
Toast.toast(withText: message).show(withAlignment: .bottom, pinToSafeArea: false)
|
Toast.show(withText: message, alignment: .bottom, pinToSafeArea: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ final class DonationView: UIView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension NSLayoutConstraint {
|
extension NSLayoutConstraint {
|
||||||
func withPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
|
func withPriority(_ priority: UILayoutPriority) -> NSLayoutConstraint {
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ extension PlacePageInteractor: PlacePageInfoViewControllerDelegate {
|
|||||||
UIPasteboard.general.string = content
|
UIPasteboard.general.string = content
|
||||||
let message = String(format: L("copied_to_clipboard"), content)
|
let message = String(format: L("copied_to_clipboard"), content)
|
||||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||||
Toast.toast(withText: message).show(withAlignment: .bottom)
|
Toast.show(withText: message, alignment: .bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func didPressOpenInApp(from sourceView: UIView) {
|
func didPressOpenInApp(from sourceView: UIView) {
|
||||||
|
|||||||
@@ -254,12 +254,12 @@ static NSString * const kUDDidShowICloudSynchronizationEnablingAlert = @"kUDDidS
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MWMBookmarksShareStatusEmptyCategory:
|
case MWMBookmarksShareStatusEmptyCategory:
|
||||||
[[MWMToast toastWithText:L(@"bookmarks_error_title_share_empty")] show];
|
[Toast showWithText:L(@"bookmarks_error_title_share_empty")];
|
||||||
isEnabled(NO);
|
isEnabled(NO);
|
||||||
break;
|
break;
|
||||||
case MWMBookmarksShareStatusArchiveError:
|
case MWMBookmarksShareStatusArchiveError:
|
||||||
case MWMBookmarksShareStatusFileError:
|
case MWMBookmarksShareStatusFileError:
|
||||||
[[MWMToast toastWithText:L(@"dialog_routing_system_error")] show];
|
[Toast showWithText:L(@"dialog_routing_system_error")];
|
||||||
isEnabled(NO);
|
isEnabled(NO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user