From 129597568c3b1a5fef26be4ae14f498d1f586a40 Mon Sep 17 00:00:00 2001 From: Daniela Date: Thu, 20 May 2021 13:34:27 +0100 Subject: [PATCH 1/2] Increase customisation --- Sources/Slider.swift | 193 +++++++++++++++++++++++----------------- Sources/ValueView.swift | 34 ++++--- 2 files changed, 133 insertions(+), 94 deletions(-) diff --git a/Sources/Slider.swift b/Sources/Slider.swift index 08f47ce..27ee138 100644 --- a/Sources/Slider.swift +++ b/Sources/Slider.swift @@ -60,8 +60,8 @@ open class Slider : UIControl { addSubview(contentView) contentView.addSubview(backgroundImageView) - contentView.addSubview(minimumImageView) - contentView.addSubview(maximumImageView) + contentView.addSubview(minimumImageView) + contentView.addSubview(maximumImageView) contentView.addSubview(minimumLabel) contentView.addSubview(maximumLabel) contentView.addSubview(valueView) @@ -81,15 +81,15 @@ open class Slider : UIControl { open var fraction: CGFloat = 0 { didSet { updateValueViewText() - layoutValueView() + layoutValueView() } } - open var showFractionOnlyWhileTracking = false { - didSet { - updateValueViewText() - } - } + open var showFractionOnlyWhileTracking = false { + didSet { + updateValueViewText() + } + } open var attributedTextForFraction: (CGFloat) -> (NSAttributedString) = { fraction in let formatter = NumberFormatter() @@ -99,78 +99,103 @@ open class Slider : UIControl { return NSAttributedString(string: string) } - open var valueViewMargin: CGFloat = ValueView.kLayoutMarginInset { - didSet { - if valueViewMargin < ValueView.kLayoutMarginInset { - valueViewMargin = ValueView.kLayoutMarginInset - } - layoutValueView() - } - } + open var valueViewMargin: CGFloat = ValueView.kLayoutMarginInset { + didSet { + if valueViewMargin < ValueView.kLayoutMarginInset { + valueViewMargin = ValueView.kLayoutMarginInset + } + layoutValueView() + } + } private let valueView = ValueView() + open var valueViewTextColor: UIColor? { + didSet { + updateValueViewTextColor() + } + } + open var valueViewColor: UIColor? { didSet { updateValueViewColor() } } - private func updateValueViewColor() { - valueView.outerFillColor = contentViewColor - valueView.innerFillColor = valueViewColor + open var valueViewBorderColor: UIColor? { + didSet { + updateValueViewBorderColor() + } + } + + open var valueViewSelectedTextColor: UIColor? + + open var valueViewBorderSelectedColor: UIColor? + + open var valueViewSelectedColor: UIColor? + + private func updateValueViewColor(useSelectedColor: Bool = false) { + valueView.innerFillColor = useSelectedColor ? valueViewSelectedColor : valueViewColor + } + + private func updateValueViewBorderColor(useSelectedColor: Bool = false) { + valueView.outerFillColor = useSelectedColor ? valueViewBorderSelectedColor : valueViewBorderColor + } + + private func updateValueViewTextColor(useSelectedColor: Bool = false) { + valueView.textColor = useSelectedColor ? valueViewSelectedTextColor : valueViewTextColor } - open var isAnimationEnabled = true - private(set) open var isSliderTracking = false + open var isAnimationEnabled = true + private(set) open var isSliderTracking = false private func updateValueViewText() { - if !showFractionOnlyWhileTracking || isSliderTracking { - let text = attributedTextForFraction(fraction) - valueView.attributedText = text - } else { - valueView.attributedText = nil - } + if !showFractionOnlyWhileTracking || isSliderTracking { + let text = attributedTextForFraction(fraction) + valueView.attributedText = text + } else { + valueView.attributedText = nil + } } - // MARK: - Images + // MARK: - Images - private let minimumImageView = UIImageView() - private let maximumImageView = UIImageView() + private let minimumImageView = UIImageView() + private let maximumImageView = UIImageView() - open var imagesMargin: CGFloat = 10 { - didSet { - layoutImageViews() - } - } + open var imagesMargin: CGFloat = 10 { + didSet { + layoutImageViews() + } + } - open var imagesColor: UIColor? { - didSet { - minimumImageView.tintColor = imagesColor - maximumImageView.tintColor = imagesColor - } - } + open var imagesColor: UIColor? { + didSet { + minimumImageView.tintColor = imagesColor + maximumImageView.tintColor = imagesColor + } + } - open func setMinimumImage(_ image: UIImage?) { - minimumImageView.image = image?.withRenderingMode(.alwaysTemplate) - layoutImageViews() - } + open func setMinimumImage(_ image: UIImage?) { + minimumImageView.image = image?.withRenderingMode(.alwaysTemplate) + layoutImageViews() + } - open func setMaximumImage(_ image: UIImage?) { - maximumImageView.image = image?.withRenderingMode(.alwaysTemplate) - layoutImageViews() - } + open func setMaximumImage(_ image: UIImage?) { + maximumImageView.image = image?.withRenderingMode(.alwaysTemplate) + layoutImageViews() + } // MARK: - Labels private let minimumLabel = UILabel() private let maximumLabel = UILabel() - open var labelsMargin: CGFloat = 10 { - didSet { - layoutLabelsText() - } - } + open var labelsMargin: CGFloat = 10 { + didSet { + layoutLabelsText() + } + } open func setMinimumLabelAttributedText(_ attributedText: NSAttributedString?) { minimumLabel.attributedText = attributedText @@ -186,11 +211,11 @@ open class Slider : UIControl { private let backgroundImageView = UIImageView() - open var contentViewCornerRadius: CGFloat = 8 { - didSet { - layoutBackgroundImage() - } - } + open var contentViewCornerRadius: CGFloat = 8 { + didSet { + layoutBackgroundImage() + } + } open var contentViewColor: UIColor? { didSet { @@ -226,7 +251,7 @@ open class Slider : UIControl { filterView.mask?.frame = filterView.bounds layoutBackgroundImage() - layoutImageViews() + layoutImageViews() layoutLabelsText() layoutValueView() } @@ -239,22 +264,22 @@ open class Slider : UIControl { maximumLabel.frame = CGRect(x: bounds.maxX - labelsMargin - maximumLabel.bounds.width, y: bounds.midY - maximumLabel.bounds.midY, width: maximumLabel.bounds.width, height: maximumLabel.bounds.height).integral } - private func layoutImageViews() { - let imageInset = ValueView.kLayoutMarginInset * 2 - let imageSize = CGSize(width: bounds.height - imageInset * 2, height: bounds.height - imageInset * 2) + private func layoutImageViews() { + let imageInset = ValueView.kLayoutMarginInset * 2 + let imageSize = CGSize(width: bounds.height - imageInset * 2, height: bounds.height - imageInset * 2) - minimumImageView.frame = CGRect(x: imagesMargin, y: imageInset, width: imageSize.width, height: imageSize.height).integral - minimumImageView.contentMode = .left - if let image = minimumImageView.image, image.size.width > minimumImageView.bounds.width || image.size.height > minimumImageView.bounds.height { - minimumImageView.contentMode = .scaleAspectFit - } + minimumImageView.frame = CGRect(x: imagesMargin, y: imageInset, width: imageSize.width, height: imageSize.height).integral + minimumImageView.contentMode = .left + if let image = minimumImageView.image, image.size.width > minimumImageView.bounds.width || image.size.height > minimumImageView.bounds.height { + minimumImageView.contentMode = .scaleAspectFit + } - maximumImageView.frame = CGRect(x: bounds.maxX - imagesMargin - imageSize.width, y: imageInset, width: imageSize.width, height: imageSize.height).integral - maximumImageView.contentMode = .right - if let image = maximumImageView.image, image.size.width > maximumImageView.bounds.width || image.size.height > maximumImageView.bounds.height { - maximumImageView.contentMode = .scaleAspectFit - } - } + maximumImageView.frame = CGRect(x: bounds.maxX - imagesMargin - imageSize.width, y: imageInset, width: imageSize.width, height: imageSize.height).integral + maximumImageView.contentMode = .right + if let image = maximumImageView.image, image.size.width > maximumImageView.bounds.width || image.size.height > maximumImageView.bounds.height { + maximumImageView.contentMode = .scaleAspectFit + } + } private func layoutBackgroundImage() { let inset = UIEdgeInsets(top: min(0, shadowOffset.height - shadowBlur), left: min(0, shadowOffset.width - shadowBlur), bottom: max(0, shadowOffset.height + shadowBlur) * -1, right: max(0, shadowOffset.width + shadowBlur) * -1) @@ -284,18 +309,21 @@ open class Slider : UIControl { override open func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { let result = super.beginTracking(touch, with: event) let x = touch.location(in: self).x - isSliderTracking = true + isSliderTracking = true fraction = fractionForPositionX(x) valueView.animateTrackingBegin() sendActions(for: .valueChanged) didBeginTracking?(self) + updateValueViewColor(useSelectedColor: true) + updateValueViewBorderColor(useSelectedColor: true) + updateValueViewTextColor(useSelectedColor: true) return result } override open func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { let result = super.continueTracking(touch, with: event) let x = touch.location(in: self).x - isSliderTracking = true + isSliderTracking = true fraction = fractionForPositionX(x) filterView.center.x = valueView.center.x sendActions(for: .valueChanged) @@ -304,18 +332,22 @@ open class Slider : UIControl { override open func endTracking(_ touch: UITouch?, with event: UIEvent?) { super.endTracking(touch, with: event) - isSliderTracking = false + isSliderTracking = false valueView.animateTrackingEnd() - updateValueViewText() + updateValueViewText() didEndTracking?(self) + updateValueViewColor() + updateValueViewBorderColor() + updateValueViewTextColor() } override open func cancelTracking(with event: UIEvent?) { super.cancelTracking(with: event) - isSliderTracking = false + isSliderTracking = false valueView.animateTrackingEnd() - updateValueViewText() + updateValueViewText() didEndTracking?(self) + updateValueViewColor() } private func boundsForValueViewCenter() -> CGRect { @@ -381,5 +413,4 @@ open class Slider : UIControl { (filterView.mask as? UIImageView)?.image = filterViewMask } } - } diff --git a/Sources/ValueView.swift b/Sources/ValueView.swift index 342881a..6b9a9e1 100644 --- a/Sources/ValueView.swift +++ b/Sources/ValueView.swift @@ -11,7 +11,7 @@ import pop class ValueView : UIView { - static let kLayoutMarginInset: CGFloat = 4 + static let kLayoutMarginInset: CGFloat = 1 // MARK: - Initialization @@ -54,6 +54,12 @@ class ValueView : UIView { } } + var textColor: UIColor? { + didSet { + textLabel.textColor = textColor + } + } + // MARK: - Text Label private let textLabel = UILabel() @@ -63,17 +69,20 @@ class ValueView : UIView { return textLabel.attributedText } set { - if let newValue = newValue { - // apply centered horizontal alignment - let string = newValue.mutableCopy() as! NSMutableAttributedString - let paragraph = (string.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle ?? NSParagraphStyle()).mutableCopy() as! NSMutableParagraphStyle - paragraph.alignment = .center - string.addAttribute(.paragraphStyle, value: paragraph, range: NSMakeRange(0, string.length)) - textLabel.attributedText = string - } else { - textLabel.attributedText = nil - } - } + if let newValue = newValue { + // apply centered horizontal alignment + let string = newValue.mutableCopy() as! NSMutableAttributedString + let paragraph = (string.attribute(.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle ?? NSParagraphStyle()).mutableCopy() as! NSMutableParagraphStyle + paragraph.alignment = .center + string.addAttribute(.paragraphStyle, value: paragraph, range: NSMakeRange(0, string.length)) + if let textColor = textColor { + string.addAttribute(.foregroundColor, value: textColor, range: NSMakeRange(0, string.length)) + } + textLabel.attributedText = string + } else { + textLabel.attributedText = nil + } + } } // MARK: - Laying out Subviews @@ -119,5 +128,4 @@ class ValueView : UIView { shapeView.layer.pop_add(animation, forKey: "bounce") } } - } From 2ce34896ca3f1d51037589453e5891eb18db482a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ota=CC=81vio=20Zabaleta?= Date: Tue, 27 Jul 2021 23:53:25 +0100 Subject: [PATCH 2/2] Fix for when the user tries to scroll a table/collection view which cells contain a Slider. When the user tries to scroll the ScrollView beyond it's content size (causing the bounce effect), sometimes the Slider gets a call to Slider.beginTracking, followed by a Slider.cancelTracking. Since the body of Slider.cancelTracking only calls updateValueViewColor(), sometimes the ValueView textcolor ends up the same as it's background color, in which case the text becomes invisible. --- Sources/Slider.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Slider.swift b/Sources/Slider.swift index 27ee138..af3364f 100644 --- a/Sources/Slider.swift +++ b/Sources/Slider.swift @@ -348,6 +348,8 @@ open class Slider : UIControl { updateValueViewText() didEndTracking?(self) updateValueViewColor() + updateValueViewBorderColor() + updateValueViewTextColor() } private func boundsForValueViewCenter() -> CGRect {