Skip to content

Make rotating of cards customizable #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ DerivedData
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Pods/
ZLSwipeableViewSwiftDemo/Pods/
Pods/

# Carthage
#
Expand Down
6 changes: 3 additions & 3 deletions ZLSwipeableViewSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "ZLSwipeableViewSwift"
s.version = "0.0.7"
s.version = "0.0.8"
s.summary = "A simple view for building card like interface like Tinder and Potluck."
s.description = <<-DESC
ZLSwipeableViewSwift is a simple view for building card like interface like Tinder and Potluck.
Expand All @@ -11,9 +11,9 @@ Pod::Spec.new do |s|
s.author = { "Zhixuan Lai" => "[email protected]" }
s.social_media_url = "http://twitter.com/ZhixuanLai"

s.platform = :ios, "8.0"
s.platform = :ios, "9.0"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason this change to drop support for iOS 8?


s.source = { :git => "https://github.com/zhxnlai/ZLSwipeableViewSwift.git", :tag => "0.0.7" }
s.source = { :git => "https://github.com/zhxnlai/ZLSwipeableViewSwift.git", :tag => "0.0.8" }
s.source_files = "ZLSwipeableViewSwift/*.swift"

s.framework = "UIKit"
Expand Down
6 changes: 3 additions & 3 deletions ZLSwipeableViewSwift/Direction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public func ==(lhs: Direction, rhs: Direction) -> Bool {
/**
* Swiped direction.
*/
public struct Direction : OptionSetType, CustomStringConvertible {
public struct Direction : OptionSet, CustomStringConvertible {

public var rawValue: UInt

Expand All @@ -35,7 +35,7 @@ public struct Direction : OptionSetType, CustomStringConvertible {
public static let Vertical: Direction = [Up, Down]
public static let All: Direction = [Horizontal, Vertical]

public static func fromPoint(point: CGPoint) -> Direction {
public static func fromPoint(_ point: CGPoint) -> Direction {
switch (point.x, point.y) {
case let (x, y) where abs(x) >= abs(y) && x > 0:
return .Right
Expand Down Expand Up @@ -73,4 +73,4 @@ public struct Direction : OptionSetType, CustomStringConvertible {
}
}

}
}
12 changes: 6 additions & 6 deletions ZLSwipeableViewSwift/Scheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ class Scheduler : NSObject {
typealias Action = () -> Void
typealias EndCondition = () -> Bool

var timer: NSTimer?
var timer: Timer?
var action: Action?
var endCondition: EndCondition?

func scheduleRepeatedly(action: Action, interval: NSTimeInterval, endCondition: EndCondition) {
func scheduleRepeatedly(_ action: @escaping Action, interval: TimeInterval, endCondition: @escaping EndCondition) {
guard timer == nil && interval > 0 else { return }
self.action = action
self.endCondition = endCondition
timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: #selector(Scheduler.doAction(_:)), userInfo: nil, repeats: true)
timer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(Scheduler.doAction(_:)), userInfo: nil, repeats: true)
}

func doAction(timer: NSTimer) {
guard let action = action, let endCondition = endCondition where !endCondition() else {
func doAction(_ timer: Timer) {
guard let action = action, let endCondition = endCondition, !endCondition() else {
timer.invalidate()
self.timer = nil
self.action = nil
Expand All @@ -35,4 +35,4 @@ class Scheduler : NSObject {
action()
}

}
}
141 changes: 73 additions & 68 deletions ZLSwipeableViewSwift/ViewManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ class ViewManager : NSObject {
// Snapping -> [Moving]+ -> Snapping
// Snapping -> [Moving]+ -> Swiping -> Snapping
enum State {
case Snapping(CGPoint), Moving(CGPoint), Swiping(CGPoint, CGVector)
case snapping(CGPoint), moving(CGPoint), swiping(CGPoint, CGVector)
}

var state: State {
didSet {
if case .Snapping(_) = oldValue, case let .Moving(point) = state {
if case .snapping(_) = oldValue, case let .moving(point) = state {
unsnapView()
attachView(toPoint: point)
} else if case .Snapping(_) = oldValue, case let .Swiping(origin, direction) = state {
} else if case .snapping(_) = oldValue, case let .swiping(origin, direction) = state {
unsnapView()
attachView(toPoint: origin)
pushView(fromPoint: origin, inDirection: direction)
} else if case .Moving(_) = oldValue, case let .Moving(point) = state {
} else if case .moving(_) = oldValue, case let .moving(point) = state {
moveView(toPoint: point)
} else if case .Moving(_) = oldValue, case let .Snapping(point) = state {
} else if case .moving(_) = oldValue, case let .snapping(point) = state {
detachView()
snapView(point)
} else if case .Moving(_) = oldValue, case let .Swiping(origin, direction) = state {
} else if case .moving(_) = oldValue, case let .swiping(origin, direction) = state {
pushView(fromPoint: origin, inDirection: direction)
} else if case .Swiping(_, _) = oldValue, case let .Snapping(point) = state {
} else if case .swiping(_, _) = oldValue, case let .snapping(point) = state {
unpushView()
detachView()
snapView(point)
Expand All @@ -41,46 +41,49 @@ class ViewManager : NSObject {
}

/// To be added to view and removed
private class ZLPanGestureRecognizer: UIPanGestureRecognizer { }
private class ZLTapGestureRecognizer: UITapGestureRecognizer { }
fileprivate class ZLPanGestureRecognizer: UIPanGestureRecognizer { }
fileprivate class ZLTapGestureRecognizer: UITapGestureRecognizer { }

static private let anchorViewWidth = CGFloat(1000)
private var anchorView = UIView(frame: CGRect(x: 0, y: 0, width: anchorViewWidth, height: anchorViewWidth))
static fileprivate let anchorViewWidth = CGFloat(1000)
fileprivate var anchorView = UIView(frame: CGRect(x: 0, y: 0, width: anchorViewWidth, height: anchorViewWidth))

private var snapBehavior: UISnapBehavior!
private var viewToAnchorViewAttachmentBehavior: UIAttachmentBehavior!
private var anchorViewToPointAttachmentBehavior: UIAttachmentBehavior!
private var pushBehavior: UIPushBehavior!

private let view: UIView
private let containerView: UIView
private let miscContainerView: UIView
private let animator: UIDynamicAnimator
private weak var swipeableView: ZLSwipeableView?

init(view: UIView, containerView: UIView, index: Int, miscContainerView: UIView, animator: UIDynamicAnimator, swipeableView: ZLSwipeableView) {
fileprivate var snapBehavior: UISnapBehavior!
fileprivate var viewToAnchorViewAttachmentBehavior: UIAttachmentBehavior!
fileprivate var anchorViewToPointAttachmentBehavior: UIAttachmentBehavior!
fileprivate var pushBehavior: UIPushBehavior!

fileprivate let view: UIView
fileprivate let containerView: UIView
fileprivate let miscContainerView: UIView
fileprivate let animator: UIDynamicAnimator
fileprivate let rotatingSpeedMultiplyer: CGFloat

fileprivate weak var swipeableView: ZLSwipeableView?

init(view: UIView, containerView: UIView, index: Int, miscContainerView: UIView, animator: UIDynamicAnimator, swipeableView: ZLSwipeableView, rotatingSpeedMultiplyer rotMul: CGFloat) {
self.view = view
self.containerView = containerView
self.miscContainerView = miscContainerView
self.animator = animator
self.swipeableView = swipeableView
self.state = ViewManager.defaultSnappingState(view)
self.rotatingSpeedMultiplyer = rotMul
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well, Multiplyer should be spelt Multiplier 👍

Copy link
Collaborator

@AndrewSB AndrewSB Sep 29, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and anywhere else in the file where you've used or defined a variable with that in it's name


super.init()

view.addGestureRecognizer(ZLPanGestureRecognizer(target: self, action: #selector(ViewManager.handlePan(_:))))
view.addGestureRecognizer(ZLTapGestureRecognizer(target: self, action: #selector(ViewManager.handleTap(_:))))
miscContainerView.addSubview(anchorView)
containerView.insertSubview(view, atIndex: index)
containerView.insertSubview(view, at: index)
}

static func defaultSnappingState(view: UIView) -> State {
return .Snapping(view.convertPoint(view.center, fromView: view.superview))
static func defaultSnappingState(_ view: UIView) -> State {
return .snapping(view.convert(view.center, from: view.superview))
}

func snappingStateAtContainerCenter() -> State {
guard let swipeableView = swipeableView else { return ViewManager.defaultSnappingState(view) }
return .Snapping(containerView.convertPoint(swipeableView.center, fromView: swipeableView.superview))
return .snapping(containerView.convert(swipeableView.center, from: swipeableView.superview))
}

deinit {
Expand All @@ -98,7 +101,7 @@ class ViewManager : NSObject {
}

for gestureRecognizer in view.gestureRecognizers! {
if gestureRecognizer.isKindOfClass(ZLPanGestureRecognizer.classForCoder()) {
if gestureRecognizer.isKind(of: ZLPanGestureRecognizer.classForCoder()) {
view.removeGestureRecognizer(gestureRecognizer)
}
}
Expand All @@ -107,108 +110,110 @@ class ViewManager : NSObject {
view.removeFromSuperview()
}

func handlePan(recognizer: UIPanGestureRecognizer) {
func handlePan(_ recognizer: UIPanGestureRecognizer) {
guard let swipeableView = swipeableView else { return }

let translation = recognizer.translationInView(containerView)
let location = recognizer.locationInView(containerView)
let velocity = recognizer.velocityInView(containerView)
let translation = recognizer.translation(in: containerView)
let location = recognizer.location(in: containerView)
let velocity = recognizer.velocity(in: containerView)
let movement = Movement(location: location, translation: translation, velocity: velocity)

switch recognizer.state {
case .Began:
guard case .Snapping(_) = state else { return }
state = .Moving(location)
swipeableView.didStart?(view: view, atLocation: location)
case .Changed:
guard case .Moving(_) = state else { return }
state = .Moving(location)
swipeableView.swiping?(view: view, atLocation: location, translation: translation)
case .Ended, .Cancelled:
guard case .Moving(_) = state else { return }
if swipeableView.shouldSwipeView(view: view, movement: movement, swipeableView: swipeableView) {
case .began:
guard case .snapping(_) = state else { return }
state = .moving(location)
swipeableView.didStart?(view, location)
case .changed:
guard case .moving(_) = state else { return }
state = .moving(location)
swipeableView.swiping?(view, location, translation)
case .ended, .cancelled:
guard case .moving(_) = state else { return }
if swipeableView.shouldSwipeView(view, movement, swipeableView) {
let directionVector = CGVector(point: translation.normalized * max(velocity.magnitude, swipeableView.minVelocityInPointPerSecond))
state = .Swiping(location, directionVector)
state = .swiping(location, directionVector)
swipeableView.swipeView(view, location: location, directionVector: directionVector)
} else {
state = snappingStateAtContainerCenter()
swipeableView.didCancel?(view: view)
swipeableView.didCancel?(view)
}
swipeableView.didEnd?(view: view, atLocation: location)
swipeableView.didEnd?(view, location)
default:
break
}
}

func handleTap(recognizer: UITapGestureRecognizer) {
guard let swipeableView = swipeableView, topView = swipeableView.topView() else { return }
func handleTap(_ recognizer: UITapGestureRecognizer) {
guard let swipeableView = swipeableView, let topView = swipeableView.topView() else { return }

let location = recognizer.locationInView(containerView)
swipeableView.didTap?(view: topView, atLocation: location)
let location = recognizer.location(in: containerView)
swipeableView.didTap?(topView, location)
}

private func snapView(point: CGPoint) {
snapBehavior = UISnapBehavior(item: view, snapToPoint: point)
fileprivate func snapView(_ point: CGPoint) {
snapBehavior = UISnapBehavior(item: view, snapTo: point)
snapBehavior!.damping = 0.75
addBehavior(snapBehavior)
}

private func unsnapView() {
fileprivate func unsnapView() {
guard let snapBehavior = snapBehavior else { return }
removeBehavior(snapBehavior)
}

private func attachView(toPoint point: CGPoint) {
fileprivate func attachView(toPoint point: CGPoint) {
anchorView.center = point
anchorView.backgroundColor = UIColor.blueColor()
anchorView.hidden = true


// attach aView to anchorView
let p = view.center
viewToAnchorViewAttachmentBehavior = UIAttachmentBehavior(item: view, offsetFromCenter: UIOffset(horizontal: -(p.x - point.x), vertical: -(p.y - point.y)), attachedToItem: anchorView, offsetFromCenter: UIOffsetZero)
viewToAnchorViewAttachmentBehavior!.length = 0
let x = (point.x - p.x) * rotatingSpeedMultiplyer + p.x
let y = (point.y - p.y) * rotatingSpeedMultiplyer + p.y
let newPoint = CGPoint(x: x, y: y)

viewToAnchorViewAttachmentBehavior = UIAttachmentBehavior.pinAttachment(with: view, attachedTo: anchorView, attachmentAnchor: newPoint)


// attach anchorView to point
anchorViewToPointAttachmentBehavior = UIAttachmentBehavior(item: anchorView, offsetFromCenter: UIOffsetZero, attachedToAnchor: point)
anchorViewToPointAttachmentBehavior = UIAttachmentBehavior(item: anchorView, offsetFromCenter: UIOffset.zero, attachedToAnchor: point)
anchorViewToPointAttachmentBehavior!.damping = 100
anchorViewToPointAttachmentBehavior!.length = 0

addBehavior(viewToAnchorViewAttachmentBehavior!)
addBehavior(anchorViewToPointAttachmentBehavior!)
}

private func moveView(toPoint point: CGPoint) {
fileprivate func moveView(toPoint point: CGPoint) {
guard let _ = viewToAnchorViewAttachmentBehavior, let toPoint = anchorViewToPointAttachmentBehavior else { return }
toPoint.anchorPoint = point
}

private func detachView() {
fileprivate func detachView() {
guard let viewToAnchorViewAttachmentBehavior = viewToAnchorViewAttachmentBehavior, let anchorViewToPointAttachmentBehavior = anchorViewToPointAttachmentBehavior else { return }
removeBehavior(viewToAnchorViewAttachmentBehavior)
removeBehavior(anchorViewToPointAttachmentBehavior)
}

private func pushView(fromPoint point: CGPoint, inDirection direction: CGVector) {
fileprivate func pushView(fromPoint point: CGPoint, inDirection direction: CGVector) {
guard let _ = viewToAnchorViewAttachmentBehavior, let anchorViewToPointAttachmentBehavior = anchorViewToPointAttachmentBehavior else { return }

removeBehavior(anchorViewToPointAttachmentBehavior)

pushBehavior = UIPushBehavior(items: [anchorView], mode: .Instantaneous)
pushBehavior = UIPushBehavior(items: [anchorView], mode: .instantaneous)
pushBehavior.pushDirection = direction
addBehavior(pushBehavior)
}

private func unpushView() {
fileprivate func unpushView() {
guard let pushBehavior = pushBehavior else { return }
removeBehavior(pushBehavior)
}

private func addBehavior(behavior: UIDynamicBehavior) {
fileprivate func addBehavior(_ behavior: UIDynamicBehavior) {
animator.addBehavior(behavior)
}

private func removeBehavior(behavior: UIDynamicBehavior) {
fileprivate func removeBehavior(_ behavior: UIDynamicBehavior) {
animator.removeBehavior(behavior)
}

}
}
Loading