Skip to content

Commit

Permalink
add deleteAtPosition method (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
mac-gallagher authored Jul 13, 2020
1 parent 8ae0c30 commit 2be4f20
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 121 deletions.
4 changes: 2 additions & 2 deletions Shuffle-iOS.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "Shuffle-iOS"
s.version = "0.3.2"
s.version = "0.3.3"
s.platform = :ios, "9.0"
s.summary = "A multi-directional card swiping library inspired by Tinder"

Expand All @@ -13,7 +13,7 @@ s.homepage = "https://github.com/mac-gallagher/Shuffle"
s.documentation_url = "https://github.com/mac-gallagher/Shuffle/tree/master/README.md"
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { "Mac Gallagher" => "[email protected]" }
s.source = { :git => "https://github.com/mac-gallagher/Shuffle.git", :tag => "v0.3.2" }
s.source = { :git => "https://github.com/mac-gallagher/Shuffle.git", :tag => "v0.3.3" }

s.swift_version = "5.0"
s.source_files = "Sources/**/*.{h,swift}"
Expand Down
22 changes: 18 additions & 4 deletions Sources/Shuffle/SwipeCardStack/CardStackStateManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@

import Foundation

typealias Swipe = (index: Int, direction: SwipeDirection)
struct Swipe: Equatable {
var index: Int
var direction: SwipeDirection
}

protocol CardStackStateManagable {
var remainingIndices: [Int] { get }
Expand All @@ -33,6 +36,7 @@ protocol CardStackStateManagable {

func insert(_ index: Int, at position: Int)
func delete(_ index: Int)
func delete(indexAtPosition position: Int)

func swipe(_ direction: SwipeDirection)
func undoSwipe() -> Swipe?
Expand Down Expand Up @@ -67,7 +71,7 @@ class CardStackStateManager: CardStackStateManagable {

// Increment all stored indices greater than or equal to index by 1
remainingIndices = remainingIndices.map { $0 >= index ? $0 + 1 : $0 }
swipes = swipes.map { $0.index >= index ? Swipe($0.index + 1, $0.direction) : $0 }
swipes = swipes.map { $0.index >= index ? Swipe(index: $0.index + 1, direction: $0.direction) : $0 }

remainingIndices.insert(index, at: position)
}
Expand All @@ -85,13 +89,23 @@ class CardStackStateManager: CardStackStateManagable {

// Decrement all stored indices greater than or equal to index by 1
remainingIndices = remainingIndices.map { $0 >= index ? $0 - 1 : $0 }
swipes = swipes.map { $0.index >= index ? Swipe($0.index - 1, $0.direction) : $0 }
swipes = swipes.map { $0.index >= index ? Swipe(index: $0.index - 1, direction: $0.direction) : $0 }
}

func delete(indexAtPosition position: Int) {
precondition(position >= 0, "Attempt to delete card at position \(position)")
//swiftlint:disable:next line_length
precondition(position < remainingIndices.count, "Attempt to delete card at position \(position), but there are only \(remainingIndices.count) cards remaining in the stack before the update")

// Decrement all stored indices greater than or equal to index by 1
let index = remainingIndices.remove(at: position)
remainingIndices = remainingIndices.map { $0 >= index ? $0 - 1 : $0 }
}

func swipe(_ direction: SwipeDirection) {
if remainingIndices.isEmpty { return }
let firstIndex = remainingIndices.removeFirst()
let swipe = Swipe(direction: direction, index: firstIndex)
let swipe = Swipe(index: firstIndex, direction: direction)
swipes.append(swipe)
}

Expand Down
82 changes: 53 additions & 29 deletions Sources/Shuffle/SwipeCardStack/SwipeCardStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@

import UIKit

/// A typealias for a `SwipeCard` and it's corresponding index in the card stack's `dataSource`.
typealias Card = (index: Int, card: SwipeCard)

open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegate {

/// A internal structure for a `SwipeCard` and it's corresponding index in the card stack's `dataSource`.
struct Card {
var index: Int
var card: SwipeCard
}

open var animationOptions: CardStackAnimatableOptions = CardStackAnimationOptions.default

/// Return `false` if you wish to ignore all horizontal gestures on the card stack.
Expand Down Expand Up @@ -201,7 +204,7 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
if (stateManager.remainingIndices.count - visibleCards.count) > 0 {
let bottomCardIndex = stateManager.remainingIndices[visibleCards.count]
if let card = loadCard(at: bottomCardIndex) {
insertCard(Card(bottomCardIndex, card), at: visibleCards.count)
insertCard(Card(index: bottomCardIndex, card: card), at: visibleCards.count)
}
}

Expand Down Expand Up @@ -290,6 +293,34 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
return nil
}

func reloadVisibleCards() {
visibleCards.forEach { $0.card.removeFromSuperview() }
visibleCards.removeAll()

let numberOfCards = min(stateManager.remainingIndices.count, numberOfVisibleCards)
for position in 0..<numberOfCards {
let index = stateManager.remainingIndices[position]
if let card = loadCard(at: index) {
insertCard(Card(index: index, card: card), at: position)
}
}
}

func insertCard(_ value: Card, at position: Int) {
cardContainer.insertSubview(value.card, at: visibleCards.count - position)
layoutCard(value.card, at: position)
visibleCards.insert(value, at: position)
}

func loadCard(at index: Int) -> SwipeCard? {
let card = dataSource?.cardStack(self, cardForIndexAt: index)
card?.delegate = self
card?.panGestureRecognizer.delegate = self
return card
}

// MARK: - State Management

/// Returns the current position of the card at the specified index.
///
/// A returned value of `0` indicates that the card is the topmost card in the stack.
Expand All @@ -309,8 +340,7 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
/// Inserts a new card with the given index at the specified position.
/// - Parameters:
/// - index: The index of the card in the data source.
/// - position: The position of the new card in the card stack. This position should be determined on
/// the returned value of `numberOfRemainingCards`.
/// - position: The position of the new card in the card stack.
public func insertCard(atIndex index: Int, position: Int) {
guard let dataSource = dataSource else { return }

Expand Down Expand Up @@ -351,8 +381,8 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
reloadVisibleCards()
}

/// Deletes the card at the specified index. Removes swipes
/// - Parameter index: The index of the card in the data source
/// Deletes the card at the specified index.
/// - Parameter index: The index of the card in the data source.
public func deleteCard(atIndex index: Int) {
guard let dataSource = dataSource else { return }

Expand All @@ -371,30 +401,24 @@ open class SwipeCardStack: UIView, SwipeCardDelegate, UIGestureRecognizerDelegat
reloadVisibleCards()
}

func reloadVisibleCards() {
visibleCards.forEach { $0.card.removeFromSuperview() }
visibleCards.removeAll()
/// Deletes the card at the specified position in the card stack.
/// - Parameter position: The position of the card to delete in the card stack.
public func deleteCard(atPosition position: Int) {
guard let dataSource = dataSource else { return }

let numberOfCards = min(stateManager.remainingIndices.count, numberOfVisibleCards)
for position in 0..<numberOfCards {
let index = stateManager.remainingIndices[position]
if let card = loadCard(at: index) {
insertCard(Card(index, card), at: position)
}
}
}
let oldNumberOfCards = stateManager.totalIndexCount
let newNumberOfCards = dataSource.numberOfCards(in: self)

func insertCard(_ value: Card, at position: Int) {
cardContainer.insertSubview(value.card, at: visibleCards.count - position)
layoutCard(value.card, at: position)
visibleCards.insert(value, at: position)
}
stateManager.delete(indexAtPosition: position)

func loadCard(at index: Int) -> SwipeCard? {
let card = dataSource?.cardStack(self, cardForIndexAt: index)
card?.delegate = self
card?.panGestureRecognizer.delegate = self
return card
if newNumberOfCards != oldNumberOfCards - 1 {
let errorString = StringUtils.createInvalidUpdateErrorString(newCount: newNumberOfCards,
oldCount: oldNumberOfCards,
deletedCount: 1)
fatalError(errorString)
}

reloadVisibleCards()
}

// MARK: - Notifications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,20 @@ class MockCardStackStateManager: CardStackStateManagable {
insertPositions.append(position)
}

var deleteCalled: Bool = false
var deleteIndex: Int?
var deleteAtIndexCalled: Bool = false
var deleteAtIndexIndex: Int?

func delete(_ index: Int) {
deleteCalled = true
deleteIndex = index
deleteAtIndexCalled = true
deleteAtIndexIndex = index
}

var deleteAtPositionCalled: Bool = false
var deleteAtPositionPosition: Int?

func delete(indexAtPosition position: Int) {
deleteAtPositionCalled = true
deleteAtPositionPosition = position
}

var swipeCalled = false
Expand Down
Loading

0 comments on commit 2be4f20

Please sign in to comment.