From 31e9596d8254a5dd60b217f759619d89d5352f9d Mon Sep 17 00:00:00 2001 From: Mihai Seremet Date: Fri, 7 Apr 2023 13:01:11 +0300 Subject: [PATCH 1/3] Add scroll indicator offset in delegate --- DMScrollBar/DMScrollBar.swift | 14 +++++- DMScrollBar/DMScrollBarConfiguration.swift | 7 +++ DMScrollBar/DMScrollBarDelegate.swift | 54 ++++++++++++++++++++-- DMScrollBar/ScrollBarIndicator.swift | 7 ++- Example/DMScrollBar/ViewController.swift | 8 ++-- 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/DMScrollBar/DMScrollBar.swift b/DMScrollBar/DMScrollBar.swift index 3ca17c1..539f9bb 100644 --- a/DMScrollBar/DMScrollBar.swift +++ b/DMScrollBar/DMScrollBar.swift @@ -494,6 +494,10 @@ public class DMScrollBar: UIView { return panGestureRecognizer?.state.isInactive == true } + private func scrollIndicatorOffset(forContentOffset contentOffset: CGFloat) -> CGFloat { + return contentOffset + scrollIndicatorTopOffset.y + infoView.frame.height / 2 + } + private func startHideTimerIfNeeded() { guard isPanGestureInactive else { return } invalidateHideTimer() @@ -513,7 +517,10 @@ public class DMScrollBar: UIView { private func updateAdditionalInfoViewState(forScrollOffset scrollViewOffset: CGFloat, previousOffset: CGFloat?) { if configuration.infoLabel == nil { return } - guard let offsetLabelText = delegate?.infoLabelText(forOffset: scrollViewOffset) else { return animateAdditionalInfoViewHide() } + guard let offsetLabelText = delegate?.infoLabelText( + forContentOffset: scrollViewOffset, + scrollIndicatorOffset: scrollIndicatorOffset(forContentOffset: scrollViewOffset) + ) else { return animateAdditionalInfoViewHide() } animateAdditionalInfoViewShow() let direction: CATransitionSubtype? = { guard let previousOffset else { return nil } @@ -533,7 +540,10 @@ public class DMScrollBar: UIView { }() scrollIndicator.updateScrollIndicatorText( direction: direction, - scrollBarLabelText: delegate?.scrollBarText(forOffset: scrollViewOffset), + scrollBarLabelText: delegate?.scrollBarText( + forContentOffset: scrollViewOffset, + scrollIndicatorOffset: scrollIndicatorOffset(forContentOffset: scrollViewOffset) + ), textConfig: textConfig ) } diff --git a/DMScrollBar/DMScrollBarConfiguration.swift b/DMScrollBar/DMScrollBarConfiguration.swift index bf1b634..edef66e 100644 --- a/DMScrollBar/DMScrollBarConfiguration.swift +++ b/DMScrollBar/DMScrollBarConfiguration.swift @@ -180,6 +180,9 @@ extension DMScrollBar.Configuration { /// Scroll bar indicator insets public let insets: UIEdgeInsets + /// Scroll bar indicator content insets + public let contentInsets: UIEdgeInsets + /// Scroll bar image public let image: UIImage? @@ -193,6 +196,7 @@ extension DMScrollBar.Configuration { /// - size: Size of the scroll bar indicator, which is placed on the right side /// - backgroundColor: Background color of the scroll bar indicator /// - insets: Scroll bar indicator insets + /// - contentInsets: Scroll bar indicator content insets /// - image: Scroll bar image /// - imageSize: Scroll bar image size. If a nil image is passed - this parameter is ignored /// - roundedCorners: Scroll bar indicator corners which should be rounded @@ -200,6 +204,7 @@ extension DMScrollBar.Configuration { size: CGSize = CGSize(width: 34, height: 34), backgroundColor: UIColor = UIColor.defaultScrollBarBackground, insets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0), + contentInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), image: UIImage? = UIImage(systemName: "arrow.up.and.down.circle")?.withRenderingMode(.alwaysOriginal).withTintColor(UIColor.systemBackground), imageSize: CGSize = CGSize(width: 20, height: 20), roundedCorners: RoundedCorners = .roundedLeftCorners @@ -207,6 +212,7 @@ extension DMScrollBar.Configuration { self.size = size self.backgroundColor = backgroundColor self.insets = insets + self.contentInsets = contentInsets self.image = image self.imageSize = imageSize self.roundedCorners = roundedCorners @@ -221,6 +227,7 @@ extension DMScrollBar.Configuration { size: .init(width: width, height: 100), backgroundColor: UIColor.label.withAlphaComponent(0.35), insets: .init(top: 4, left: 0, bottom: 4, right: 2), + contentInsets: .zero, image: nil, roundedCorners: .allRounded ) diff --git a/DMScrollBar/DMScrollBarDelegate.swift b/DMScrollBar/DMScrollBarDelegate.swift index b3ac336..1b30233 100644 --- a/DMScrollBar/DMScrollBarDelegate.swift +++ b/DMScrollBar/DMScrollBarDelegate.swift @@ -2,20 +2,22 @@ import UIKit public protocol DMScrollBarDelegate: AnyObject { /// This method is triggered every time when scroll bar offset changes while the user is dragging it - /// - Parameter offset: Scroll view content offset + /// - Parameter contentOffset: Scroll view content offset + /// - Parameter scrollIndicatorOffset: Scroll indicator offset /// - Returns: Text to present in info label (which appears during indicator scrolling to the left of the Scroll Bar) . If returning nil - the info label will not show - func infoLabelText(forOffset offset: CGFloat) -> String? + func infoLabelText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? /// This method is triggered every time when scroll bar offset changes while the user is dragging it - /// - Parameter offset: Scroll view content offset + /// - Parameter contentOffset: Scroll view content offset + /// - Parameter scrollIndicatorOffset: Scroll indicator offset /// - Returns: Text to present in scroll bar label. This method is not triggered when Configuration.Indicator.StateConfig.TextConfig is nil. - func scrollBarText(forOffset offset: CGFloat) -> String? + func scrollBarText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? } // MARK: - DMScrollBarDelegate extension for Table View public extension DMScrollBarDelegate { - func scrollBarText(forOffset offset: CGFloat) -> String? { nil } + func scrollBarText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? { nil } /// This is a convenience method to get the header title for the section at the specified content offset. This method will not work for table views with custom headers /// - Parameters: @@ -43,4 +45,46 @@ public extension DMScrollBarDelegate { return minY...maxY ~= offset } } + + /// This is a convenience method to get section index for specified collection view content offset + /// - Parameters: + /// - collectionView: Collection view in which the scroll bar is located + /// - offset: Collection View content offset + /// - Returns: Section index in collection view for specified collection view content offset + func sectionIndex(in collectionView: UICollectionView, forOffset offset: CGFloat) -> Int? { + (0.. CGPoint? { + let indexPath = IndexPath(item: 0, section: section) + let kind = UICollectionView.elementKindSectionHeader + guard isValid(indexPath: indexPath, in: collectionView) else { return nil } + guard let attributes = collectionView.layoutAttributesForSupplementaryElement( + ofKind: kind, + at: indexPath + ) else { return nil } + + return attributes.frame.origin + } + + private func isValid(indexPath: IndexPath, in collectionView: UICollectionView) -> Bool { + guard indexPath.section < collectionView.numberOfSections else { return false } + let numberOfItems = collectionView.numberOfItems(inSection: indexPath.section) + + return indexPath.row < numberOfItems || numberOfItems == 0 + } } diff --git a/DMScrollBar/ScrollBarIndicator.swift b/DMScrollBar/ScrollBarIndicator.swift index 718a2d9..b33e143 100644 --- a/DMScrollBar/ScrollBarIndicator.swift +++ b/DMScrollBar/ScrollBarIndicator.swift @@ -54,10 +54,9 @@ final class ScrollBarIndicator: UIView { centerX.isActive = true indicatorImageLabelStackView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true } - let defaultInset: CGFloat = 8 let leadingInset: CGFloat = { - guard let textConfig else { return 0 } - return stateConfig.image == nil ? textConfig.insets.left : defaultInset + guard let textConfig else { return stateConfig.contentInsets.left } + return stateConfig.image == nil ? textConfig.insets.left : stateConfig.contentInsets.left }() setupConstraint( constraint: &indicatorImageLabelStackViewLeadingConstraint, @@ -67,7 +66,7 @@ final class ScrollBarIndicator: UIView { setupConstraint( constraint: &indicatorImageLabelStackViewTrailingConstraint, build: { trailingAnchor.constraint(equalTo: indicatorImageLabelStackView.trailingAnchor, constant: $0) }, - value: textConfig != nil ? textConfig?.insets.right ?? defaultInset : 0 + value: textConfig?.insets.right ?? stateConfig.contentInsets.right ) setupIndicatorImageViewState(image: stateConfig.image, size: stateConfig.imageSize) setupIndicatorLabelState(config: textConfig) diff --git a/Example/DMScrollBar/ViewController.swift b/Example/DMScrollBar/ViewController.swift index 8809cec..e2321cd 100644 --- a/Example/DMScrollBar/ViewController.swift +++ b/Example/DMScrollBar/ViewController.swift @@ -203,13 +203,13 @@ extension ViewController: UITableViewDataSource { extension ViewController: DMScrollBarDelegate { /// In this example, this method returns the section header title for the top visible section - func infoLabelText(forOffset offset: CGFloat) -> String? { - headerTitle(in: tableView, forOffset: offset) + func infoLabelText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? { + headerTitle(in: tableView, forOffset: contentOffset) } /// In this example, this method returns the section header title for the top visible section - func scrollBarText(forOffset offset: CGFloat) -> String? { - guard let section = sectionIndex(in: tableView, forOffset: offset) else { return nil } + func scrollBarText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? { + guard let section = sectionIndex(in: tableView, forOffset: contentOffset) else { return nil } return shortHeaderDateFormatter.string(from: sections[section].date).capitalized } } From d71d6bce6e480b681b722c7400b1e35bd53675c0 Mon Sep 17 00:00:00 2001 From: Mihai Seremet Date: Fri, 7 Apr 2023 14:56:52 +0300 Subject: [PATCH 2/3] Animate indicator layout --- DMScrollBar/DMScrollBar.swift | 6 +++- DMScrollBar/DMScrollBarConfiguration.swift | 32 ++++++++++++++++++++-- DMScrollBar/ScrollBarIndicator.swift | 22 +++++++++------ DMScrollBar/ScrollBarInfoView.swift | 6 ++-- DMScrollBar/Utils/UILabel+Utils.swift | 4 +-- 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/DMScrollBar/DMScrollBar.swift b/DMScrollBar/DMScrollBar.swift index 539f9bb..0f4a623 100644 --- a/DMScrollBar/DMScrollBar.swift +++ b/DMScrollBar/DMScrollBar.swift @@ -113,7 +113,11 @@ public class DMScrollBar: UIView { scrollIndicatorTopConstraint = scrollIndicator.topAnchor.constraint(equalTo: topAnchor, constant: topOffset) scrollIndicatorTopConstraint?.isActive = true } - scrollIndicator.setup(stateConfig: stateConfig, textConfig: indicatorTextConfig) + scrollIndicator.setup( + stateConfig: stateConfig, + textConfig: indicatorTextConfig, + accessibilityIdentifier: configuration.indicator.accessibilityIdentifier + ) } private func setupAdditionalInfoView() { diff --git a/DMScrollBar/DMScrollBarConfiguration.swift b/DMScrollBar/DMScrollBarConfiguration.swift index edef66e..ed91c4c 100644 --- a/DMScrollBar/DMScrollBarConfiguration.swift +++ b/DMScrollBar/DMScrollBarConfiguration.swift @@ -71,6 +71,9 @@ extension DMScrollBar.Configuration { /// Top left and bottom left corners will be rounded by a radius equal to half the view's height public static let roundedLeftCorners = RoundedCorners(radius: .rounded, corners: [.topLeft, .bottomLeft]) + /// Top right and bottom right corners will be rounded by a radius equal to half the view's height + public static let roundedRightCorners = RoundedCorners(radius: .rounded, corners: [.topRight, .bottomRight]) + /// All corners will be rounded by a radius equal to half the view's height public static let allRounded = RoundedCorners(radius: .rounded, corners: Set(Corner.allCases)) @@ -147,24 +150,30 @@ extension DMScrollBar.Configuration { /// Scroll bar indicator show / hide animation settings public let animation: Animation + /// Accessibility identifier of the indicator + public let accessibilityIdentifier: String? + /// - Parameters: /// - normalState: Configuration for indicator state while the user is not interacting with it /// - activeState: Configuration for indicator state while the user interacting with it /// - stateChangeAnimationDuration: Time in seconds for the state change animation to take place /// - insetsFollowsSafeArea: Indicates if safe area insets should be taken into account /// - animation: Scroll bar indicator show / hide animation settings + /// - accessibilityIdentifier: Accessibility identifier of the indicator public init( normalState: StateConfig = .default, activeState: ActiveStateConfig = .unchanged, stateChangeAnimationDuration: TimeInterval = 0.3, insetsFollowsSafeArea: Bool = true, - animation: Animation = .default + animation: Animation = .default, + accessibilityIdentifier: String? = nil ) { self.normalState = normalState self.activeState = activeState self.stateChangeAnimationDuration = stateChangeAnimationDuration self.insetsFollowsSafeArea = insetsFollowsSafeArea self.animation = animation + self.accessibilityIdentifier = accessibilityIdentifier } /// Default indicator configuration @@ -189,6 +198,9 @@ extension DMScrollBar.Configuration { /// Scroll bar image size public let imageSize: CGSize + /// Accessibility identifier of the image + public let imageAccessibilityIdentifier: String? + /// Scroll bar indicator corners which should be rounded public let roundedCorners: RoundedCorners @@ -199,6 +211,7 @@ extension DMScrollBar.Configuration { /// - contentInsets: Scroll bar indicator content insets /// - image: Scroll bar image /// - imageSize: Scroll bar image size. If a nil image is passed - this parameter is ignored + /// - imageAccessibilityIdentifier: Accessibility identifier of the image /// - roundedCorners: Scroll bar indicator corners which should be rounded public init( size: CGSize = CGSize(width: 34, height: 34), @@ -207,6 +220,7 @@ extension DMScrollBar.Configuration { contentInsets: UIEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8), image: UIImage? = UIImage(systemName: "arrow.up.and.down.circle")?.withRenderingMode(.alwaysOriginal).withTintColor(UIColor.systemBackground), imageSize: CGSize = CGSize(width: 20, height: 20), + imageAccessibilityIdentifier: String? = nil, roundedCorners: RoundedCorners = .roundedLeftCorners ) { self.size = size @@ -215,6 +229,7 @@ extension DMScrollBar.Configuration { self.contentInsets = contentInsets self.image = image self.imageSize = imageSize + self.imageAccessibilityIdentifier = imageAccessibilityIdentifier self.roundedCorners = roundedCorners } @@ -252,19 +267,24 @@ extension DMScrollBar.Configuration { public let font: UIFont /// Text color of the label public let color: UIColor + /// Accessibility identifier of the label + public let accessibilityIdentifier: String? /// - Parameters: /// - insets: Text label insets from /// - font: Font that should be used for text /// - color: Text color of the label + /// - accessibilityIdentifier: Accessibility identifier of the label public init( insets: UIEdgeInsets, font: UIFont, - color: UIColor + color: UIColor, + accessibilityIdentifier: String? = nil ) { self.insets = insets self.font = font self.color = color + self.accessibilityIdentifier = accessibilityIdentifier } } } @@ -295,6 +315,9 @@ extension DMScrollBar.Configuration { /// Info label show/hide animation settings public let animation: Animation + /// Accessibility identifier of the info label + public let accessibilityIdentifier: String? + /// - Parameters: /// - font: Indicates the font that should be used for info label, which appears during indicator scrolling /// - textColor: Text color of the info label @@ -304,6 +327,7 @@ extension DMScrollBar.Configuration { /// - maximumWidth: Indicates maximum width of info label. If nil is passed - the info label will grow maximum to the leading side of the screen /// - roundedCorners: Info label corenrs which should be rounded /// - animation: Info label show/hide animation settings + /// - accessibilityIdentifier: Accessibility identifier of the info label public init( font: UIFont = UIFont.systemFont(ofSize: 13), textColor: UIColor = UIColor.systemBackground, @@ -312,7 +336,8 @@ extension DMScrollBar.Configuration { textInsets: UIEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10), maximumWidth: CGFloat? = nil, roundedCorners: RoundedCorners = .allRounded, - animation: Animation = .default + animation: Animation = .default, + accessibilityIdentifier: String? = nil ) { self.font = font self.textColor = textColor @@ -322,6 +347,7 @@ extension DMScrollBar.Configuration { self.maximumWidth = maximumWidth self.roundedCorners = roundedCorners self.animation = animation + self.accessibilityIdentifier = accessibilityIdentifier } /// Default info label configuration diff --git a/DMScrollBar/ScrollBarIndicator.swift b/DMScrollBar/ScrollBarIndicator.swift index b33e143..628fd70 100644 --- a/DMScrollBar/ScrollBarIndicator.swift +++ b/DMScrollBar/ScrollBarIndicator.swift @@ -39,8 +39,11 @@ final class ScrollBarIndicator: UIView { func setup( stateConfig: DMScrollBar.Configuration.Indicator.StateConfig, - textConfig: DMScrollBar.Configuration.Indicator.ActiveStateConfig.TextConfig? + textConfig: DMScrollBar.Configuration.Indicator.ActiveStateConfig.TextConfig?, + accessibilityIdentifier: String? = nil ) { + self.accessibilityIdentifier = accessibilityIdentifier + self.isAccessibilityElement = false backgroundColor = stateConfig.backgroundColor layer.maskedCorners = stateConfig.roundedCorners.corners.cornerMask layer.cornerRadius = cornerRadius( @@ -68,7 +71,7 @@ final class ScrollBarIndicator: UIView { build: { trailingAnchor.constraint(equalTo: indicatorImageLabelStackView.trailingAnchor, constant: $0) }, value: textConfig?.insets.right ?? stateConfig.contentInsets.right ) - setupIndicatorImageViewState(image: stateConfig.image, size: stateConfig.imageSize) + setupIndicatorImageViewState(config: stateConfig) setupIndicatorLabelState(config: textConfig) } @@ -79,29 +82,31 @@ final class ScrollBarIndicator: UIView { ) { guard let scrollBarLabelText = scrollBarLabelText, textConfig != nil else { return hideIndicatorLabel() } if scrollBarLabelText == indicatorLabel?.text { return } - indicatorLabel?.setup(text: scrollBarLabelText, direction: direction) - indicatorImageLabelStackView.layoutIfNeeded() + let animationDuration: TimeInterval = 0.15 + indicatorLabel?.setup(text: scrollBarLabelText, direction: direction, duration: animationDuration) + UIView.animate(withDuration: animationDuration, animations: layoutIfNeeded) generateHapticFeedback(style: .light) } // MARK: - Private - private func setupIndicatorImageViewState(image: UIImage?, size: CGSize) { + private func setupIndicatorImageViewState(config: DMScrollBar.Configuration.Indicator.StateConfig) { buildIndicatorImageViewIfNeeded() - if let image { + if let image = config.image { indicatorImage?.isHidden = false indicatorImage?.alpha = 1 indicatorImage?.image = image + indicatorImage?.accessibilityIdentifier = config.imageAccessibilityIdentifier setupConstraint( constraint: &indicatorImageWidthConstraint, build: indicatorImage?.widthAnchor.constraint(equalToConstant:), - value: size.width, + value: config.imageSize.width, priority: .init(999) ) setupConstraint( constraint: &indicatorImageHeightConstraint, build: indicatorImage?.heightAnchor.constraint(equalToConstant:), - value: size.height + value: config.imageSize.height ) } else { indicatorImage?.isHidden = true @@ -115,6 +120,7 @@ final class ScrollBarIndicator: UIView { showIndicatorLabel() indicatorLabel?.font = config.font indicatorLabel?.textColor = config.color + indicatorLabel?.accessibilityIdentifier = config.accessibilityIdentifier indicatorImageLabelStackView.spacing = config.insets.left } else { hideIndicatorLabel() diff --git a/DMScrollBar/ScrollBarInfoView.swift b/DMScrollBar/ScrollBarInfoView.swift index 02054cf..26f7510 100644 --- a/DMScrollBar/ScrollBarInfoView.swift +++ b/DMScrollBar/ScrollBarInfoView.swift @@ -33,6 +33,7 @@ final class ScrollBarInfoView: UIView { offsetLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -textInsets.bottom).isActive = true offsetLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: textInsets.left).isActive = true offsetLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -textInsets.right).isActive = true + offsetLabel.accessibilityIdentifier = config.accessibilityIdentifier backgroundColor = config.backgroundColor layer.maskedCorners = config.roundedCorners.corners.cornerMask @@ -50,8 +51,9 @@ final class ScrollBarInfoView: UIView { func updateText(text: String, direction: CATransitionSubtype?) { if text == offsetLabel.text { return } - offsetLabel.setup(text: text, direction: direction) - layoutIfNeeded() + let animationDuration: TimeInterval = 0.15 + offsetLabel.setup(text: text, direction: direction, duration: animationDuration) + UIView.animate(withDuration: animationDuration, animations: layoutIfNeeded) generateHapticFeedback(style: .light) } } diff --git a/DMScrollBar/Utils/UILabel+Utils.swift b/DMScrollBar/Utils/UILabel+Utils.swift index cfcf1c0..b1288a6 100644 --- a/DMScrollBar/Utils/UILabel+Utils.swift +++ b/DMScrollBar/Utils/UILabel+Utils.swift @@ -2,13 +2,13 @@ import UIKit import QuartzCore extension UILabel { - func setup(text: String, direction: CATransitionSubtype?) { + func setup(text: String, direction: CATransitionSubtype?, duration: TimeInterval) { if let direction { let animation = CATransition() animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) animation.type = CATransitionType.push animation.subtype = direction - animation.duration = 0.15 + animation.duration = duration layer.add(animation, forKey: "pushTextChange") } self.text = text From 2a8b82d83b5fefdddcfb2c0cab45ff05b18f3122 Mon Sep 17 00:00:00 2001 From: Mihai Seremet Date: Mon, 10 Apr 2023 11:36:59 +0300 Subject: [PATCH 3/3] Remove animation, add delegate default impl --- DMScrollBar/DMScrollBarDelegate.swift | 2 ++ DMScrollBar/ScrollBarIndicator.swift | 5 ++--- DMScrollBar/ScrollBarInfoView.swift | 5 ++--- DMScrollBar/Utils/UILabel+Utils.swift | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DMScrollBar/DMScrollBarDelegate.swift b/DMScrollBar/DMScrollBarDelegate.swift index 1b30233..7b2e639 100644 --- a/DMScrollBar/DMScrollBarDelegate.swift +++ b/DMScrollBar/DMScrollBarDelegate.swift @@ -17,6 +17,8 @@ public protocol DMScrollBarDelegate: AnyObject { // MARK: - DMScrollBarDelegate extension for Table View public extension DMScrollBarDelegate { + func infoLabelText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? { nil } + func scrollBarText(forContentOffset contentOffset: CGFloat, scrollIndicatorOffset: CGFloat) -> String? { nil } /// This is a convenience method to get the header title for the section at the specified content offset. This method will not work for table views with custom headers diff --git a/DMScrollBar/ScrollBarIndicator.swift b/DMScrollBar/ScrollBarIndicator.swift index 628fd70..8be7835 100644 --- a/DMScrollBar/ScrollBarIndicator.swift +++ b/DMScrollBar/ScrollBarIndicator.swift @@ -82,9 +82,8 @@ final class ScrollBarIndicator: UIView { ) { guard let scrollBarLabelText = scrollBarLabelText, textConfig != nil else { return hideIndicatorLabel() } if scrollBarLabelText == indicatorLabel?.text { return } - let animationDuration: TimeInterval = 0.15 - indicatorLabel?.setup(text: scrollBarLabelText, direction: direction, duration: animationDuration) - UIView.animate(withDuration: animationDuration, animations: layoutIfNeeded) + indicatorLabel?.setup(text: scrollBarLabelText, direction: direction) + indicatorImageLabelStackView.layoutIfNeeded() generateHapticFeedback(style: .light) } diff --git a/DMScrollBar/ScrollBarInfoView.swift b/DMScrollBar/ScrollBarInfoView.swift index 26f7510..d18b35f 100644 --- a/DMScrollBar/ScrollBarInfoView.swift +++ b/DMScrollBar/ScrollBarInfoView.swift @@ -51,9 +51,8 @@ final class ScrollBarInfoView: UIView { func updateText(text: String, direction: CATransitionSubtype?) { if text == offsetLabel.text { return } - let animationDuration: TimeInterval = 0.15 - offsetLabel.setup(text: text, direction: direction, duration: animationDuration) - UIView.animate(withDuration: animationDuration, animations: layoutIfNeeded) + offsetLabel.setup(text: text, direction: direction) + layoutIfNeeded() generateHapticFeedback(style: .light) } } diff --git a/DMScrollBar/Utils/UILabel+Utils.swift b/DMScrollBar/Utils/UILabel+Utils.swift index b1288a6..cfcf1c0 100644 --- a/DMScrollBar/Utils/UILabel+Utils.swift +++ b/DMScrollBar/Utils/UILabel+Utils.swift @@ -2,13 +2,13 @@ import UIKit import QuartzCore extension UILabel { - func setup(text: String, direction: CATransitionSubtype?, duration: TimeInterval) { + func setup(text: String, direction: CATransitionSubtype?) { if let direction { let animation = CATransition() animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) animation.type = CATransitionType.push animation.subtype = direction - animation.duration = duration + animation.duration = 0.15 layer.add(animation, forKey: "pushTextChange") } self.text = text