Skip to content

Commit

Permalink
+ Removed position property from the animator delegate method and put…
Browse files Browse the repository at this point in the history
… it into PagerCollectionViewLayoutAttributes.

+ Added scrollDirection property to support vertical scrolling.
+ Added extra properties to support more animation options.
  • Loading branch information
KelvinJin committed Feb 22, 2017
1 parent 1e24420 commit d539aee
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 28 deletions.
60 changes: 44 additions & 16 deletions Source/AnimatedCollectionViewLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,36 +20,53 @@ public class AnimatedCollectionViewLayout: UICollectionViewFlowLayout {

public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
return attributes.flatMap { $0.copy() as? UICollectionViewLayoutAttributes }.map { self.transformLayoutAttributes($0) }
return attributes.flatMap { $0.copy() as? PagerCollectionViewLayoutAttributes }.map { self.transformLayoutAttributes($0) }
}

override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
public override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
// We have to return true here so that the layout attributes would be recalculated
// everytime we scroll the collection view.
return true
}

private func transformLayoutAttributes(_ attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
private func transformLayoutAttributes(_ attributes: PagerCollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {

guard let collectionView = self.collectionView,
let a = attributes as? PagerCollectionViewLayoutAttributes else { return attributes }
guard let collectionView = self.collectionView else { return attributes }

// The position for each cell is defined as the ratio of the distance between
// the center of the cell and the center of the screen and the screen width.
// It can be negative if the cell is, for instance, on the left of the screen.
let a = attributes

let width = collectionView.frame.width
let centerX = width / 2
let offset = collectionView.contentOffset.x
let itemX = a.center.x - offset
let position = (itemX - centerX) / width
/**
The position for each cell is defined as the ratio of the distance between
the center of the cell and the center of the collectionView and the collectionView width/height
depending on the scroll direction. It can be negative if the cell is, for instance,
on the left of the screen if you're scrolling horizontally.
*/

let distance: CGFloat
let itemOffset: CGFloat

if scrollDirection == .horizontal {
distance = collectionView.frame.width
itemOffset = a.center.x - collectionView.contentOffset.x
a.startOffset = (a.frame.origin.x - collectionView.contentOffset.x) / a.frame.width
a.endOffset = (a.frame.origin.x - collectionView.contentOffset.x - collectionView.frame.width) / a.frame.width
} else {
distance = collectionView.frame.height
itemOffset = a.center.y - collectionView.contentOffset.y
a.startOffset = (a.frame.origin.y - collectionView.contentOffset.y) / a.frame.height
a.endOffset = (a.frame.origin.y - collectionView.contentOffset.y - collectionView.frame.height) / a.frame.height
}

a.scrollDirection = scrollDirection
a.middleOffset = itemOffset / distance - 0.5

// Cache the contentView since we're going to use it a lot.
if a.contentView == nil {
a.contentView = collectionView.cellForItem(at: attributes.indexPath)?.contentView
if a.contentView == nil,
let c = collectionView.cellForItem(at: attributes.indexPath)?.contentView {
a.contentView = c
}

animator?.animate(collectionView: collectionView, attributes: a, position: position)
animator?.animate(collectionView: collectionView, attributes: a)

return a
}
Expand All @@ -58,10 +75,21 @@ public class AnimatedCollectionViewLayout: UICollectionViewFlowLayout {
/// A custom layout attributes that contains extra information.
public class PagerCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
public var contentView: UIView?
public var scrollDirection: UICollectionViewScrollDirection = .vertical
public var originFrame: CGRect = .zero

public var startOffset: CGFloat = 0
public var middleOffset: CGFloat = 0
public var endOffset: CGFloat = 0

public override func copy(with zone: NSZone? = nil) -> Any {
let copy = super.copy(with: zone) as! PagerCollectionViewLayoutAttributes
copy.contentView = contentView
copy.scrollDirection = scrollDirection
copy.originFrame = originFrame
copy.startOffset = startOffset
copy.middleOffset = middleOffset
copy.endOffset = endOffset
return copy
}
}
3 changes: 2 additions & 1 deletion Source/CrossFadeAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import UIKit
public struct CrossFadeAttributeAnimator: LayoutAttributesAnimator {
public init() {}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
let contentOffset = collectionView.contentOffset
attributes.frame = CGRect(origin: contentOffset, size: attributes.frame.size)
attributes.alpha = 1 - abs(position)
Expand Down
3 changes: 2 additions & 1 deletion Source/CubeAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public struct CubeAttributeAnimator: LayoutAttributesAnimator {
self.totalAngle = totalAngle
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
if abs(position) >= 1 {
attributes.contentView?.layer.transform = CATransform3DIdentity
} else {
Expand Down
2 changes: 1 addition & 1 deletion Source/LayoutAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
import UIKit

public protocol LayoutAttributesAnimator {
func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat)
func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes)
}
3 changes: 2 additions & 1 deletion Source/LinearCardAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public struct LinearCardAttributeAnimator: LayoutAttributesAnimator {
self.scaleRate = scaleRate
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
let width = collectionView.frame.width
let transitionX = -(width * itemSpacing * position)
let scaleFactor = scaleRate - 0.1 * abs(position)
Expand Down
3 changes: 2 additions & 1 deletion Source/PageAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public struct PageAttributeAnimator: LayoutAttributesAnimator {
self.scaleRate = scaleRate
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
let contentOffset = collectionView.contentOffset
let itemOrigin = attributes.frame.origin
let scaleFactor = scaleRate * min(position, 0) + 1.0
Expand Down
27 changes: 23 additions & 4 deletions Source/ParallaxAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,33 @@ public struct ParallaxAttributesAnimator: LayoutAttributesAnimator {
self.speed = speed
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
let direction = attributes.scrollDirection

guard let contentView = attributes.contentView else { return }

if abs(position) >= 1 {
// Reset views that are invisible.
attributes.contentView?.transform = .identity
} else {
contentView.transform = .identity
} else if direction == .horizontal {
let width = collectionView.frame.width
let transitionX = -(width * speed * position)
attributes.contentView?.transform = CGAffineTransform(translationX: transitionX, y: 0)
let transform = CGAffineTransform(translationX: transitionX, y: 0)
let newFrame = attributes.bounds.applying(transform)

contentView.frame = newFrame
} else {
let height = collectionView.frame.height
let transitionY = -(height * speed * position)
let transform = CGAffineTransform(translationX: 0, y: transitionY)

// By default, the content view takes all space in the cell
let newFrame = attributes.bounds.applying(transform)

// We don't use transform here since there's an issue if layoutSubviews is called
// for every cell due to layout changes in binding method.
contentView.frame = newFrame
}
}
}
3 changes: 2 additions & 1 deletion Source/RotateInOutAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public struct RotateInOutAttributesAnimator: LayoutAttributesAnimator {
self.maxRotate = maxRotate
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
if abs(position) >= 1 {
attributes.contentView?.transform = .identity
attributes.alpha = 1.0
Expand Down
3 changes: 2 additions & 1 deletion Source/TurnAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public struct TurnAttributeAnimator: LayoutAttributesAnimator {
self.perspective = perspective
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
if abs(position) >= 1 {
attributes.contentView?.layer.transform = CATransform3DIdentity
} else {
Expand Down
3 changes: 2 additions & 1 deletion Source/ZoomInOutAttributesAnimator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public struct ZoomInOutAttributesAnimator: LayoutAttributesAnimator {
self.scaleRate = scaleRate
}

public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes, position: CGFloat) {
public func animate(collectionView: UICollectionView, attributes: PagerCollectionViewLayoutAttributes) {
let position = attributes.middleOffset
if abs(position) >= 1 {
attributes.contentView?.transform = .identity
} else {
Expand Down

0 comments on commit d539aee

Please sign in to comment.