From 7d5df6649924a57f7ef911740d02764de876bfd3 Mon Sep 17 00:00:00 2001 From: Aron Manucheri Date: Mon, 26 Feb 2018 14:02:16 +0100 Subject: [PATCH] Add Swift version to podspec file + format & lint the code Cocoapods 1.4.0 includes the ability to specify the Swift version in the podspec (previously this could be done by creating a separate file in the repo). This allows Cocoapods to know which target to set for the Pod - instead of going for a default value, which can cause troubles when mixing Swift 4 and 3 code. This change adds the Swift version to the Podspec. The other changes includes some basic formatting and linting (mostly removal of whitespace and following Swift styleguides). Also updates the Podspec version. --- KSSwipeStack.podspec | 3 +- .../PanDirectionGestureRecognizer.swift | 10 +- KSSwipeStack/Classes/SwipableData.swift | 2 +- KSSwipeStack/Classes/SwipableView.swift | 26 +++-- KSSwipeStack/Classes/SwipeDelegate.swift | 2 +- KSSwipeStack/Classes/SwipeDirection.swift | 2 +- KSSwipeStack/Classes/SwipeHelper.swift | 55 ++++----- KSSwipeStack/Classes/SwipeOptions.swift | 4 +- KSSwipeStack/Classes/SwipeView.swift | 106 +++++++++--------- 9 files changed, 106 insertions(+), 104 deletions(-) diff --git a/KSSwipeStack.podspec b/KSSwipeStack.podspec index 5ca6a58..d03787c 100644 --- a/KSSwipeStack.podspec +++ b/KSSwipeStack.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = 'KSSwipeStack' - s.version = '0.4.2' + s.version = '0.4.3' s.summary = 'A Swift card swiping library created by Kicksort Consulting AB' # This description is used to generate tags and improve search results. @@ -34,6 +34,7 @@ DOCS: https://github.com/Kicksort/KSSwipeStack s.source = { :git => 'https://github.com/kicksort/KSSwipeStack.git', :tag => s.version.to_s } s.ios.deployment_target = '8.0' + s.swift_version = '4.0' s.source_files = 'KSSwipeStack/Classes/**/*' diff --git a/KSSwipeStack/Classes/PanDirectionGestureRecognizer.swift b/KSSwipeStack/Classes/PanDirectionGestureRecognizer.swift index 34f7cf7..5d9b5ef 100644 --- a/KSSwipeStack/Classes/PanDirectionGestureRecognizer.swift +++ b/KSSwipeStack/Classes/PanDirectionGestureRecognizer.swift @@ -18,20 +18,20 @@ public enum PanDirection { public class PanDirectionGestureRecognizer: UIPanGestureRecognizer { let direction: PanDirection - + init(direction: PanDirection, target: AnyObject, action: Selector) { self.direction = direction super.init(target: target, action: action) } - - override public func touchesMoved(_ touches: Set, with event: UIEvent) { + + public override func touchesMoved(_ touches: Set, with event: UIEvent) { super.touchesMoved(touches, with: event) - + if state == .began { guard let view = self.view else { return } - + let v = velocity(in: view) switch direction { case .Horizontal where fabs(v.y) > fabs(v.x): diff --git a/KSSwipeStack/Classes/SwipableData.swift b/KSSwipeStack/Classes/SwipableData.swift index e7424b9..257429d 100644 --- a/KSSwipeStack/Classes/SwipableData.swift +++ b/KSSwipeStack/Classes/SwipableData.swift @@ -9,7 +9,7 @@ import Foundation /** -Protocol which must be implemented by all data models meant to be swiped in the card stack. + Protocol which must be implemented by all data models meant to be swiped in the card stack. */ public protocol SwipableData { /** diff --git a/KSSwipeStack/Classes/SwipableView.swift b/KSSwipeStack/Classes/SwipableView.swift index f14fd36..ec25637 100644 --- a/KSSwipeStack/Classes/SwipableView.swift +++ b/KSSwipeStack/Classes/SwipableView.swift @@ -12,57 +12,59 @@ import UIKit */ open class SwipableView: UIView { private var data: SwipableData? - + public override init(frame: CGRect) { super.init(frame: frame) } - - required public init?(coder aDecoder: NSCoder) { + + public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } + /** - Is fired when the view is being moved within the stack. + Is fired when the view is being moved within the stack. ## You can here change the visuals of the view based on: - Whether or not the card is being swiped to the right (liked) or (disliked) - The requested opacity of any overlays the view might have, based on the x-position of the view. - + - parameter like: true for like (right) and false for dislike (left) - parameter opacity: the requested opacity of overlays. Based on x-position */ open func respondToSwipe(like: Bool, opacity: Float) { } - + /** - Should contain logic to reset the view to its initial, visual state. + Should contain logic to reset the view to its initial, visual state. This is for example called when a card is released, and snaps back to the center of the view. */ open func resetView() { } - + /** This is being fired when a view is 'dimissed' from the stack. For example when it has been swiped away and left the view. */ open func respondToDismiss() { } + /** Set the data this SwipableView is a representation of. - parameter data: The data it is meant to represent. - + */ open func setData(_ data: SwipableData) { self.data = data } - + /** Get the data this SwipableView is a representation of. - returns: The data it is meant to represent. - + */ open func getData() -> SwipableData? { return data } - + /** Should return whether or not the user is to be allowed to undo swiping this card. */ diff --git a/KSSwipeStack/Classes/SwipeDelegate.swift b/KSSwipeStack/Classes/SwipeDelegate.swift index 3b9f3db..5716b11 100644 --- a/KSSwipeStack/Classes/SwipeDelegate.swift +++ b/KSSwipeStack/Classes/SwipeDelegate.swift @@ -15,4 +15,4 @@ public protocol SwipeDelegate { - parameter swipe: The swipe which just occured. */ func onNext(_ swipe: Swipe) - } +} diff --git a/KSSwipeStack/Classes/SwipeDirection.swift b/KSSwipeStack/Classes/SwipeDirection.swift index 11a7b8d..746752f 100644 --- a/KSSwipeStack/Classes/SwipeDirection.swift +++ b/KSSwipeStack/Classes/SwipeDirection.swift @@ -13,7 +13,7 @@ public enum SwipeDirection { case right case up case down - + func getSwipeEndpoint() -> CGPoint { switch self { case .left: diff --git a/KSSwipeStack/Classes/SwipeHelper.swift b/KSSwipeStack/Classes/SwipeHelper.swift index 9f64651..e5337b6 100644 --- a/KSSwipeStack/Classes/SwipeHelper.swift +++ b/KSSwipeStack/Classes/SwipeHelper.swift @@ -13,12 +13,12 @@ class SwipeHelper { private let swipeViewSize: CGSize private let swipeViewCenter: CGPoint open var options = SwipeOptions() - + init(with frame: CGRect) { swipeViewSize = frame.size swipeViewCenter = CGPoint(x: frame.width / 2, y: frame.height / 2) } - + /// Move and animate a view to a desired position /// /// - Parameters: @@ -28,7 +28,7 @@ class SwipeHelper { func move(_ card: UIView, duration: Double = 0.25, toPoint: CGPoint) { move(card, duration: duration, toPoint: toPoint, completion: {}) } - + /// Move and animate a view to a desired position /// /// - Parameters: @@ -44,6 +44,7 @@ class SwipeHelper { completion() }) } + /// Move and animate a view to a desired position, /// transforming the view according to the current SwipeOptions /// Uses dismissAnimationDuration set in SwipeOptions @@ -51,13 +52,13 @@ class SwipeHelper { /// - card: The view to be move /// - toPoint: Destination of the move action /// - completion: Callback fireing when the animation is done and the view is moved. - + func moveFastAndTransform(_ card: SwipableView, toPoint: CGPoint, completion: @escaping () -> Void) { addBorderToCard(card) - - let rotation = self.calculateRotationAnimation(cardCenter: toPoint) - let scale = self.calculateScaleAnimation(cardCenter: toPoint) - + + let rotation = calculateRotationAnimation(cardCenter: toPoint) + let scale = calculateScaleAnimation(cardCenter: toPoint) + card.respondToSwipe(like: toPoint.x > 0, opacity: toPoint.equalTo(swipeViewCenter) ? 0.0 : 1.0) UIView.animate(withDuration: options.dismissAnimationDuration, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: { card.center = toPoint @@ -67,43 +68,43 @@ class SwipeHelper { completion() }) } - + /// Calculate the magnitude if a throw based on the velocity vector /// Used when determining if a card has been thrown 'hard enough' to be dismissed. /// - Parameter velocity: velocity vector of the gesture /// - Returns: magnitude of the throw - func calculateThrowMagnitude(for velocity: CGPoint ) -> Float { + func calculateThrowMagnitude(for velocity: CGPoint) -> Float { let velXSq = Float(velocity.x) * Float(velocity.x) let velYSq = Float(velocity.y) * Float(velocity.y) return sqrtf(velXSq + velYSq) } - + /// Returns a view to its original visual state in regards to border, rotation and scale. /// Animates the reset of the view /// - Parameter card: View to reset func resetCard(_ card: UIView) { let rotation = CATransform3DMakeRotation(0, 0, 0, 1) let scale = CATransform3DMakeScale(1.0, 1.0, 1.0) - + let borderAnim = CABasicAnimation(keyPath: "borderWidth") borderAnim.fromValue = card.layer.borderWidth borderAnim.toValue = 0 borderAnim.duration = 0.1 card.layer.borderWidth = 0 - + let cornerAnim = CABasicAnimation(keyPath: "cornerRadius") cornerAnim.fromValue = card.layer.cornerRadius cornerAnim.toValue = 0 cornerAnim.duration = 0.1 card.layer.cornerRadius = 0 - + let both = CAAnimationGroup() both.duration = 0.1 both.animations = [cornerAnim, borderAnim] both.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) - + card.layer.add(both, forKey: "both") - + UIView.animate(withDuration: 0.1) { card.layer.transform = CATransform3DConcat(rotation, scale) } @@ -118,18 +119,18 @@ class SwipeHelper { card.layer.transform = CATransform3DConcat(rotation, scale) addBorderToCard(card) } - + func addBorderToCard(_ card: UIView) { card.layer.borderWidth = 10 card.layer.borderColor = UIColor.white.cgColor card.layer.cornerRadius = 10 card.layer.masksToBounds = true } - + private func calculateScaleAnimation(cardCenter: CGPoint) -> CATransform3D { let horizontalDistance = calculateHorizontalDistanceFromCenter(cardCenter) let verticalDistance = calculateVerticalDistanceFromCenter(cardCenter) - + var scaleFactor = CGFloat(0) if horizontalDistance >= verticalDistance { scaleFactor = CGFloat(1 - horizontalDistance / 800.0) @@ -138,16 +139,16 @@ class SwipeHelper { } return CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0) } - + private func calculateRotationAnimation(cardCenter: CGPoint) -> CATransform3D { let xFromCenter = Double(cardCenter.x - swipeViewSize.width / 2) - var rads = CGFloat(xFromCenter/10.0 * .pi / 180.0) + var rads = CGFloat(xFromCenter / 10.0 * .pi / 180.0) if abs(rads) > 1.4 { rads = -rads } return CATransform3DMakeRotation(rads, 0, 0, 1) } - + /// Calculates the distance in the horizontal plane from the position of a view to the center of the screen /// /// - Parameter cardCenter: A positonal coordinate, preferably the center of a view. @@ -155,7 +156,7 @@ class SwipeHelper { private func calculateHorizontalDistanceFromCenter(_ cardCenter: CGPoint) -> Double { return Double(abs(cardCenter.x - swipeViewSize.width / 2)) } - + /// Calculates the distance in the vertical plane from the position of a view to the center of the screen /// /// - Parameter cardCenter: A positonal coordinate, preferably the center of a view. @@ -163,7 +164,7 @@ class SwipeHelper { private func calculateVerticalDistanceFromCenter(_ cardCenter: CGPoint) -> Double { return Double(abs(cardCenter.y - swipeViewSize.height / 2)) } - + /// Calculate a proper destination for a dismissal of a view based on its position /// Places the view far to the left if the view is to the left the the center of the screen and vice versa. /// - Parameter card: View which endpoint to calculate @@ -171,12 +172,12 @@ class SwipeHelper { func calculateEndpoint(_ card: UIView) -> CGPoint { let deltaX = card.center.x - swipeViewSize.width / 2 let deltaY = card.center.y - swipeViewSize.height / 2 - + let k = deltaY / deltaX let toX = deltaX < 0 ? -swipeViewSize.height / 2 : swipeViewSize.width + swipeViewSize.height / 2 return CGPoint(x: toX, y: toX * k) } - + /// Calculate a proper destination for a dismissal of a view based on current velocity /// Places the view far to the left if the view is currently moving to the left and vice versa. /// The angle from the center to the proposed destination of the view is based on the angle of the velocity vector @@ -187,7 +188,7 @@ class SwipeHelper { let toX = velocity.x < 0 ? -swipeViewSize.height / 2 : swipeViewSize.width + swipeViewSize.height / 2 return CGPoint(x: toX, y: toX * k) } - + /// Converts a position with coordinates with the origin of the screen as origo to one using the center of the screen as origo. /// Can be used to convert a origin value to a center value refering to the same positioning of a full screen view. /// - Parameter center: Position using origin as origo diff --git a/KSSwipeStack/Classes/SwipeOptions.swift b/KSSwipeStack/Classes/SwipeOptions.swift index 89a7903..5d1d7ec 100644 --- a/KSSwipeStack/Classes/SwipeOptions.swift +++ b/KSSwipeStack/Classes/SwipeOptions.swift @@ -21,6 +21,6 @@ public struct SwipeOptions { public var refillThreshold = 10 public var dismissAnimationDuration = 0.25 public var freezeWhenDismissing = false - - public init(){} + + public init() {} } diff --git a/KSSwipeStack/Classes/SwipeView.swift b/KSSwipeStack/Classes/SwipeView.swift index bfe0c7b..9bd2ee5 100644 --- a/KSSwipeStack/Classes/SwipeView.swift +++ b/KSSwipeStack/Classes/SwipeView.swift @@ -6,54 +6,53 @@ // Copyright © 2017 Kicksort Consulting AB. All rights reserved. // -import UIKit import RxSwift - +import UIKit struct SwipeHistoryItem { let data: SwipableData let origin: CGPoint } -/// Represents a swipable view to be rendered in the swipe stack. +/// Represents a swipable view to be rendered in the swipe stack. /// The visual representation of a SwipeData object. public class SwipeView: UIView { private lazy var swipeHelper = SwipeHelper(with: frame) private lazy var horizontalPan = PanDirectionGestureRecognizer(direction: .Horizontal, target: self, action: #selector(respondToHorizontalPan)) private lazy var verticalPan = PanDirectionGestureRecognizer(direction: .Vertical, target: self, action: #selector(respondToVerticalPan)) - + fileprivate var dataset: [SwipableData] = [] fileprivate var renderedCards: [SwipableView] = [] fileprivate var swipeHistory: [SwipeHistoryItem] = [] fileprivate var options = SwipeOptions() - + fileprivate var swipeDelegate: SwipeDelegate? fileprivate var swipeSubject: PublishSubject? fileprivate var refillSubject: PublishSubject? - + public func setup() { setup(options: self.options, swipeDelegate: nil) } - + public func setup(options: SwipeOptions) { setup(options: options, swipeDelegate: nil) } - + public func setup(swipeDelegate: SwipeDelegate?) { setup(options: self.options, swipeDelegate: swipeDelegate) } - + public func setup(options: SwipeOptions, swipeDelegate: SwipeDelegate?) { self.options = options swipeHelper.options = options - + if let swipeDelegate = swipeDelegate { self.swipeDelegate = swipeDelegate } - + setSwipeDirections(horizontal: options.allowHorizontalSwipes, vertical: options.allowVerticalSwipes) } - + /** Sets whether it should be possible to swipe cards horizontally and/or vertically. - parameter horizontal: Set to true if the SwipeView should respond to horizontal swipes. @@ -65,14 +64,14 @@ public class SwipeView: UIView { } else { removeGestureRecognizer(horizontalPan) } - + if vertical { addGestureRecognizer(verticalPan) } else { removeGestureRecognizer(verticalPan) } } - + /** Adds a card to the stack and calls notifyDatasetUpdated to make sure it is rendered if needed. - parameter data: The data the new card represents. @@ -81,7 +80,7 @@ public class SwipeView: UIView { dataset.append(data) notifyDatasetUpdated() } - + /** Adds a card to the top of the stack and calls notifyDatasetUpdated to make sure it is rendered if needed. - parameter data: The data the new card represents. @@ -93,7 +92,7 @@ public class SwipeView: UIView { addSubview(renderedCard) bringSubview(toFront: renderedCard) } - + /** Adds a card to the top of the stack and calls notifyDatasetUpdated to make sure it is rendered if needed. - parameter data: The data the new card represents. @@ -108,7 +107,7 @@ public class SwipeView: UIView { bringSubview(toFront: renderedCard) snapBack() } - + /** Get swipe events generated by the SwipeView - returns: RxObservable firing once for each swipe @@ -120,7 +119,7 @@ public class SwipeView: UIView { swipeSubject = PublishSubject() return getSwipes() } - + /** Get notifications when the card stack has reached the refillThreshold defined in SwipeOptions - returns: RxObservable firing with the swipe which put the stack below the refillThreshold @@ -132,22 +131,21 @@ public class SwipeView: UIView { refillSubject = PublishSubject() return needsRefill() } - + // Undoes last swipe public func undoSwipe() { guard options.allowUndo else { return } - + if let cardToUndo = swipeHistory.popLast() { addCardToTop(cardToUndo.data, from: cardToUndo.origin) } } - + fileprivate func setupSwipeCards() { - } - + /** Fetch the card currently visible at the top of the stack. - returns: The top card (the currently visible) in the stack. @@ -155,7 +153,7 @@ public class SwipeView: UIView { fileprivate func getCurrentCard() -> SwipableView? { return renderedCards.first } - + /** Notify the swipe view that the dataset has changed. */ @@ -164,7 +162,7 @@ public class SwipeView: UIView { fillStack() } } - + /** Fills the card stack by rendering new cards from the dataset if needed. */ @@ -176,7 +174,7 @@ public class SwipeView: UIView { fillStack() } } - + /** Renders a Swipeble View onto the screen and puts it in the correct postion in the stack. - parameter view: The SwipeableView to render. @@ -190,7 +188,7 @@ public class SwipeView: UIView { } return view } - + /// Renders the next card from the stack func showNextCard() { if !renderedCards.isEmpty { @@ -199,14 +197,14 @@ public class SwipeView: UIView { swipedCard.removeFromSuperview() swipedCard.respondToDismiss() } - + if self.renderedCards.count < options.maxRenderedCards, !dataset.isEmpty { fillStack() } - + isUserInteractionEnabled = true } - + /// Handles horizontal pan gestures (drags) in the view /// /// - Parameter gesture: the gesture itself @@ -214,16 +212,16 @@ public class SwipeView: UIView { let translation = gesture.translation(in: self) let velocity = gesture.velocity(in: self) let magnitude = swipeHelper.calculateThrowMagnitude(for: velocity) - - if let card = getCurrentCard(){ + + if let card = getCurrentCard() { let previousOrigin = card.frame.origin let nextOrigin = CGPoint(x: self.frame.origin.x + translation.x, y: self.frame.origin.y + translation.y) - card.center = CGPoint(x: frame.width/2 + translation.x, y: frame.height/2 + translation.y) + card.center = CGPoint(x: frame.width / 2 + translation.x, y: frame.height / 2 + translation.y) swipeHelper.transformCard(card) - + let opacity = abs(Float(card.center.x.distance(to: self.center.x) / (frame.width / 4))) card.respondToSwipe(like: translation.x > 0, opacity: opacity) - + if gesture.state == .ended { let throwingThresholdExceeded = magnitude > options.throwingThreshold let panThresholdExceeded = abs(nextOrigin.x) > frame.width * options.horizontalPanThreshold @@ -247,7 +245,7 @@ public class SwipeView: UIView { } } } - + /// Handles vertical pan gestures (drags) in the view /// /// - Parameter gesture: the gesture itself @@ -255,16 +253,16 @@ public class SwipeView: UIView { let translation = gesture.translation(in: self) let velocity = gesture.velocity(in: self) let magnitude = swipeHelper.calculateThrowMagnitude(for: velocity) - - if let card = getCurrentCard(){ + + if let card = getCurrentCard() { let previousOrigin = card.frame.origin let nextOrigin = CGPoint(x: self.frame.origin.x + translation.x, y: self.frame.origin.y + translation.y) - card.center = CGPoint(x: frame.width/2 + translation.x, y: frame.height/2 + translation.y) + card.center = CGPoint(x: frame.width / 2 + translation.x, y: frame.height / 2 + translation.y) swipeHelper.transformCard(card) - + let opacity = abs(Float(card.center.y.distance(to: self.center.y) / (frame.height / 4))) card.respondToSwipe(like: translation.y > 0, opacity: opacity) - + if gesture.state == .ended { let throwingThresholdExceeded = magnitude > options.throwingThreshold let panThresholdExceeded = abs(nextOrigin.y) > frame.height * options.verticalPanThreshold @@ -288,7 +286,7 @@ public class SwipeView: UIView { } } } - + /// Handles when a view is swiped in the view /// /// - Parameters: @@ -296,14 +294,14 @@ public class SwipeView: UIView { /// - gesture: The gesture generating the swipe open func respondToSwipe(_ direction: SwipeDirection, gesture: UIGestureRecognizer? = nil) { guard let card = getCurrentCard() else { - // TODO + // TODO: return } - + if card.isUndoable(), let data = card.getData() { swipeHistory.append(SwipeHistoryItem(data: data, origin: card.frame.origin)) } - + dismissCard(direction: direction, gesture: gesture, completion: { [weak self] in let swipe = Swipe(direction: direction, data: card.getData()) if let swipeHandler = self?.swipeDelegate { @@ -317,7 +315,7 @@ public class SwipeView: UIView { } }) } - + /// Resets the currently visible view to the original position with a 'snap' effect. func snapBack() { if let currentCard = getCurrentCard() { @@ -326,21 +324,21 @@ public class SwipeView: UIView { currentCard.resetView() } } - + /// Get the number of items in the swipe view, both rendered and unrendered. /// /// - Returns: The number of items in the dataset func getDataCount() -> Int { return self.renderedCards.count + self.dataset.count } - + /// Checks if the refill threshold is surpassed /// /// - Returns: true if the data count is below the refill threshold func needsRefill() -> Bool { return getDataCount() <= options.refillThreshold } - + /// Used to dismiss a swipable view through an end position. /// Animates a movement to specified position and then dismisses the swipable view. /// - Parameters: @@ -351,17 +349,17 @@ public class SwipeView: UIView { guard let card = getCurrentCard() else { return } - + isUserInteractionEnabled = !options.freezeWhenDismissing - + var toPoint = swipeHelper.convertToCenter(origin: direction.getSwipeEndpoint()) - + if options.allowHorizontalSwipes && !options.allowVerticalSwipes { // Special case to better handle rapid flicks if !(card.frame.origin.x == 0 && card.frame.origin.y == 0) { if card.center.x > frame.width / 2 { toPoint = swipeHelper.calculateEndpoint(card) - } else if let gesture = gesture as? UIPanGestureRecognizer{ + } else if let gesture = gesture as? UIPanGestureRecognizer { let velocity = gesture.velocity(in: self) if !(velocity.x == 0 && velocity.y == 0) { toPoint = swipeHelper.calculateEndpoint(card, basedOn: velocity) @@ -369,7 +367,7 @@ public class SwipeView: UIView { } } } - + swipeHelper.moveFastAndTransform(card, toPoint: toPoint, completion: { completion() self.showNextCard()