From c6ecab830f94fbadd9796b22f714eff51336abbf Mon Sep 17 00:00:00 2001 From: Cameron Pierce Date: Mon, 5 Dec 2016 15:38:23 -0800 Subject: [PATCH 1/4] Adding the ability to pass in an image for the circle slider view. Also adding the ability to specify which direction the slider will transition from. --- Sources/SlidableImage.swift | 65 ++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/Sources/SlidableImage.swift b/Sources/SlidableImage.swift index ab9491e..8760e89 100644 --- a/Sources/SlidableImage.swift +++ b/Sources/SlidableImage.swift @@ -8,6 +8,23 @@ import UIKit +public enum SlideDirection: Int { + case left + case right + case top + case bottom +} + +public struct SlidableImageBorder { + var borderWidth: CGFloat + var borderColor: CGColor + + public init(borderWidth width: CGFloat, borderColor color: CGColor) { + self.borderWidth = width + self.borderColor = color + } +} + /// Super easy Slider for before&after images open class SlidableImage: UIView { @@ -20,16 +37,20 @@ open class SlidableImage: UIView { /// Circle view with icon for sliding images. You can override it with your custom view. open var sliderCircle: UIView + /// Enum that describes which direction the slider will slide from. + open var slideDirection: SlideDirection + /// Generic initializer with views /// /// - Parameters: /// - frame: Frame size /// - firstView: First view - should have size equal to frame and second view /// - secondView: Second view - should have size equal to frame and second view - public init(frame: CGRect, firstView: UIView, secondView: UIView) { + public init(frame: CGRect, firstView: UIView, secondView: UIView, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil) { self.firstView = firstView self.secondView = secondView - sliderCircle = SlidableImage.setupSliderCircle() + self.sliderCircle = SlidableImage.setupSliderCircle(sliderImage: sliderImage) + self.slideDirection = slideDirection ?? .right super.init(frame: frame) initializeViews() @@ -42,11 +63,11 @@ open class SlidableImage: UIView { /// - frame: Frame size /// - firstImage: First image for sliding /// - secondImage: Second image for sliding - convenience public init(frame: CGRect, firstImage: UIImage, secondImage: UIImage) { + convenience public init(frame: CGRect, firstImage: UIImage, secondImage: UIImage, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil) { let firstView = SlidableImage.setup(image: firstImage, frame: frame) let secondView = SlidableImage.setup(image: secondImage, frame: frame) - - self.init(frame: frame, firstView: firstView, secondView: secondView) + + self.init(frame: frame, firstView: firstView, secondView: secondView, sliderImage: sliderImage, slideDirection: slideDirection) } required public init?(coder aDecoder: NSCoder) { @@ -57,14 +78,32 @@ open class SlidableImage: UIView { /// /// - Parameter maskLocation: Position of slider open func updateMask(location maskLocation: CGFloat) { - let maskRectPath = UIBezierPath(rect: CGRect(x: bounds.minX, + var maskRectPath: UIBezierPath + let mask = CAShapeLayer() + switch slideDirection { + case .left: + maskRectPath = UIBezierPath(rect: CGRect(x: maskLocation, + y: bounds.minY, + width: bounds.width, + height: bounds.height)) + case .right: + maskRectPath = UIBezierPath(rect: CGRect(x: bounds.minX, y: bounds.minY, width: maskLocation, height: bounds.height)) - let mask = CAShapeLayer() + case .top: + maskRectPath = UIBezierPath(rect: CGRect(x: bounds.minX, + y: maskLocation, + width: bounds.width, + height: bounds.height)) + case .bottom: + maskRectPath = UIBezierPath(rect: CGRect(x: bounds.minX, + y: bounds.minY, + width: bounds.width, + height: maskLocation)) + } mask.path = maskRectPath.cgPath secondView.layer.mask = mask - sliderCircle.center.x = maskLocation } @@ -98,14 +137,16 @@ open class SlidableImage: UIView { /// Private wrapper for setup circle slider view /// /// - Returns: Slider circle - private class func setupSliderCircle() -> UIView { + private class func setupSliderCircle(sliderImage: UIImage? = nil) -> UIView { // Workaround - without this view, gesture recognizer doesn't work let circle = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50)) circle.layer.cornerRadius = circle.bounds.width / 2 - - let imageView = UIImageView(image: UIImage(named: "slider")) + + let imageView = UIImageView(image: sliderImage) + imageView.contentMode = .scaleAspectFill circle.addSubview(imageView) - + imageView.center = circle.center + return circle } From e7f04e3fbf579722ad2bccb0854ed295c8133713 Mon Sep 17 00:00:00 2001 From: Cameron Pierce Date: Mon, 5 Dec 2016 15:50:03 -0800 Subject: [PATCH 2/4] Forgot to update the checks for the new y directions. --- Sources/SlidableImage.swift | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Sources/SlidableImage.swift b/Sources/SlidableImage.swift index 8760e89..79d8ca6 100644 --- a/Sources/SlidableImage.swift +++ b/Sources/SlidableImage.swift @@ -104,15 +104,23 @@ open class SlidableImage: UIView { } mask.path = maskRectPath.cgPath secondView.layer.mask = mask - sliderCircle.center.x = maskLocation + if slideDirection == .left || slideDirection == .right { + sliderCircle.center.x = maskLocation + } else { + sliderCircle.center.y = maskLocation + } } /// Private wrapper for setup view fileprivate func initializeViews() { clipsToBounds = true sliderCircle.center = center - updateMask(location: center.x) - + if slideDirection == .left || slideDirection == .right { + updateMask(location: center.x) + } else { + updateMask(location: center.y) + } + addSubview(firstView) addSubview(secondView) addSubview(sliderCircle) @@ -127,11 +135,13 @@ open class SlidableImage: UIView { @objc private func gestureHandler(_ panGestureRecognizer: UIPanGestureRecognizer) { let location = panGestureRecognizer.location(in: firstView) - guard (bounds.minX...bounds.maxX ~= location.x) else { - return + if slideDirection == .left || slideDirection == .right { + guard (bounds.minX...bounds.maxX ~= location.x) else { return } + updateMask(location: location.x) + } else { + guard (bounds.minY...bounds.maxY ~= location.y) else { return } + updateMask(location: location.y) } - - updateMask(location: location.x) } /// Private wrapper for setup circle slider view From 20c23073a7a920ea19b4b33f17cb4a807656053e Mon Sep 17 00:00:00 2001 From: Cameron Pierce Date: Thu, 22 Dec 2016 18:51:11 -0800 Subject: [PATCH 3/4] Adding the option to have a border to the slidable image. --- Sources/SlidableImage.swift | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/Sources/SlidableImage.swift b/Sources/SlidableImage.swift index 79d8ca6..edac867 100644 --- a/Sources/SlidableImage.swift +++ b/Sources/SlidableImage.swift @@ -17,9 +17,9 @@ public enum SlideDirection: Int { public struct SlidableImageBorder { var borderWidth: CGFloat - var borderColor: CGColor - - public init(borderWidth width: CGFloat, borderColor color: CGColor) { + var borderColor: UIColor + + public init(borderWidth width: CGFloat, borderColor color: UIColor) { self.borderWidth = width self.borderColor = color } @@ -39,18 +39,23 @@ open class SlidableImage: UIView { /// Enum that describes which direction the slider will slide from. open var slideDirection: SlideDirection - + + /// Struct that tells the slider if there should be a border view added + open var sliderBorder: SlidableImageBorder? = nil + private var sliderBorderView: UIView? + /// Generic initializer with views /// /// - Parameters: /// - frame: Frame size /// - firstView: First view - should have size equal to frame and second view /// - secondView: Second view - should have size equal to frame and second view - public init(frame: CGRect, firstView: UIView, secondView: UIView, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil) { + public init(frame: CGRect, firstView: UIView, secondView: UIView, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil, sliderBorder: SlidableImageBorder? = nil) { self.firstView = firstView self.secondView = secondView self.sliderCircle = SlidableImage.setupSliderCircle(sliderImage: sliderImage) self.slideDirection = slideDirection ?? .right + self.sliderBorder = sliderBorder super.init(frame: frame) initializeViews() @@ -63,11 +68,11 @@ open class SlidableImage: UIView { /// - frame: Frame size /// - firstImage: First image for sliding /// - secondImage: Second image for sliding - convenience public init(frame: CGRect, firstImage: UIImage, secondImage: UIImage, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil) { + convenience public init(frame: CGRect, firstImage: UIImage, secondImage: UIImage, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil, sliderBorder: SlidableImageBorder? = nil) { let firstView = SlidableImage.setup(image: firstImage, frame: frame) let secondView = SlidableImage.setup(image: secondImage, frame: frame) - self.init(frame: frame, firstView: firstView, secondView: secondView, sliderImage: sliderImage, slideDirection: slideDirection) + self.init(frame: frame, firstView: firstView, secondView: secondView, sliderImage: sliderImage, slideDirection: slideDirection, sliderBorder: sliderBorder) } required public init?(coder aDecoder: NSCoder) { @@ -124,6 +129,20 @@ open class SlidableImage: UIView { addSubview(firstView) addSubview(secondView) addSubview(sliderCircle) + + if let sliderBorder = sliderBorder { + sliderBorderView = UIView() + sliderBorderView?.translatesAutoresizingMaskIntoConstraints = false + sliderBorderView?.backgroundColor = sliderBorder.borderColor + addSubview(sliderBorderView!) + addConstraints([ + NSLayoutConstraint(item: sliderBorderView!, attribute: .centerX, relatedBy: .equal, toItem: sliderCircle, attribute: .centerX, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: sliderBorderView!, attribute: .centerY, relatedBy: .equal, toItem: sliderCircle, attribute: .centerY, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: sliderBorderView!, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1.0, constant: 0.0), + NSLayoutConstraint(item: sliderBorderView!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: sliderBorder.borderWidth) + ]) + bringSubview(toFront: sliderCircle) + } } /// Private wrapper for adding gesture recognizer From 20606c335326489bd75e2213a52d633b967ed7a5 Mon Sep 17 00:00:00 2001 From: Cameron Pierce Date: Fri, 23 Dec 2016 12:30:08 -0800 Subject: [PATCH 4/4] PR changes. --- Sources/SlidableImage.swift | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/Sources/SlidableImage.swift b/Sources/SlidableImage.swift index edac867..06c7823 100644 --- a/Sources/SlidableImage.swift +++ b/Sources/SlidableImage.swift @@ -8,6 +8,7 @@ import UIKit +/// A enum used to determine which direction the slider should slide from public enum SlideDirection: Int { case left case right @@ -15,6 +16,7 @@ public enum SlideDirection: Int { case bottom } +/// A struct containing border information for the slider. You can use this struct to define the width and color of the slider border. public struct SlidableImageBorder { var borderWidth: CGFloat var borderColor: UIColor @@ -50,6 +52,8 @@ open class SlidableImage: UIView { /// - frame: Frame size /// - firstView: First view - should have size equal to frame and second view /// - secondView: Second view - should have size equal to frame and second view + /// - slideDirection: Optional parameter for the direction that the slider should slide. The default value is .right. + /// - sliderBorder: Optional paramter that will add a border to the slider of the specfied size and color public init(frame: CGRect, firstView: UIView, secondView: UIView, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil, sliderBorder: SlidableImageBorder? = nil) { self.firstView = firstView self.secondView = secondView @@ -68,6 +72,8 @@ open class SlidableImage: UIView { /// - frame: Frame size /// - firstImage: First image for sliding /// - secondImage: Second image for sliding + /// - slideDirection: Optional parameter for the direction that the slider should slide. The default value is .right. + /// - sliderBorder: Optional paramter that will add a border to the slider of the specfied size and color convenience public init(frame: CGRect, firstImage: UIImage, secondImage: UIImage, sliderImage: UIImage? = nil, slideDirection: SlideDirection? = nil, sliderBorder: SlidableImageBorder? = nil) { let firstView = SlidableImage.setup(image: firstImage, frame: frame) let secondView = SlidableImage.setup(image: secondImage, frame: frame) @@ -109,9 +115,10 @@ open class SlidableImage: UIView { } mask.path = maskRectPath.cgPath secondView.layer.mask = mask - if slideDirection == .left || slideDirection == .right { + switch slideDirection { + case .left, .right: sliderCircle.center.x = maskLocation - } else { + case .top, .bottom: sliderCircle.center.y = maskLocation } } @@ -120,9 +127,10 @@ open class SlidableImage: UIView { fileprivate func initializeViews() { clipsToBounds = true sliderCircle.center = center - if slideDirection == .left || slideDirection == .right { + switch slideDirection { + case .left, .right: updateMask(location: center.x) - } else { + case .top, .bottom: updateMask(location: center.y) } @@ -130,6 +138,7 @@ open class SlidableImage: UIView { addSubview(secondView) addSubview(sliderCircle) + // Only add the slider to the view if a non nil sliderBorder was set if let sliderBorder = sliderBorder { sliderBorderView = UIView() sliderBorderView?.translatesAutoresizingMaskIntoConstraints = false @@ -154,10 +163,11 @@ open class SlidableImage: UIView { @objc private func gestureHandler(_ panGestureRecognizer: UIPanGestureRecognizer) { let location = panGestureRecognizer.location(in: firstView) - if slideDirection == .left || slideDirection == .right { + switch slideDirection { + case .left, .right: guard (bounds.minX...bounds.maxX ~= location.x) else { return } updateMask(location: location.x) - } else { + case .top, .bottom: guard (bounds.minY...bounds.maxY ~= location.y) else { return } updateMask(location: location.y) } @@ -165,6 +175,8 @@ open class SlidableImage: UIView { /// Private wrapper for setup circle slider view /// + /// - Parameters: + /// - image: Content image for slider circle /// - Returns: Slider circle private class func setupSliderCircle(sliderImage: UIImage? = nil) -> UIView { // Workaround - without this view, gesture recognizer doesn't work