Support vertical paging.
This commit is contained in:
parent
8c649841ee
commit
de1bfbb109
|
|
@ -1,7 +1,7 @@
|
|||
Pod::Spec.new do |s|
|
||||
|
||||
s.name = "FSPagerView"
|
||||
s.version = "0.3.0"
|
||||
s.version = "0.4.0"
|
||||
s.summary = "FSPagerView is an elegant Screen Slide Library for making Banner、Product Show、Welcome/Guide Pages、Screen/ViewController Sliders."
|
||||
|
||||
s.homepage = "https://github.com/WenchaoD/FSPagerView"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.0</string>
|
||||
<string>0.4.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<key>FSPagerViewExample-Objc.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
|
|
|||
|
|
@ -7,4 +7,7 @@
|
|||
<FileRef
|
||||
location = "group:FSPagerViewExample-Objc/FSPagerViewExample-Objc.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:FSPagerView/FSPagerView.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
## Features
|
||||
* ***Infinite*** scrolling.
|
||||
* ***Automatic*** Sliding.
|
||||
* Support ***Horizontal*** and ***Vertical*** paging.
|
||||
* Fully customizable item, with predefined banner-style item.
|
||||
* Fully customizable ***page control***.
|
||||
* Rich build-in 3D transformers.
|
||||
|
|
@ -398,4 +399,6 @@ FSPageControl *pageControl = [[FSPageControl alloc] initWithFrame:frame2];
|
|||
* <img src="https://cloud.githubusercontent.com/assets/5186464/22828152/b83ab04e-efd6-11e6-9baf-37abf0ae29c0.png" style="margin-bottom:-8px"/>
|
||||
***[FSCalendar](https://github.com/WenchaoD/FSCalendar)***
|
||||
|
||||
---
|
||||
|
||||
# [Documentation](http://cocoadocs.org/docsets/FSPagerView)
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
## Features
|
||||
* ***Infinite*** scrolling.
|
||||
* ***Automatic*** Sliding.
|
||||
* ***Horizontal*** and ***Vertical*** paging.
|
||||
* Fully customizable item, with predefined banner-style item.
|
||||
* Fully customizable ***page control***.
|
||||
* Rich build-in 3D transformers.
|
||||
|
|
@ -405,5 +406,6 @@ func pagerViewDidEndDecelerating(_ pagerView: FSPagerView)
|
|||
* <img src="https://cloud.githubusercontent.com/assets/5186464/22828152/b83ab04e-efd6-11e6-9baf-37abf0ae29c0.png" style="margin-bottom:-8px"/>
|
||||
***[FSCalendar](https://github.com/WenchaoD/FSCalendar)***
|
||||
|
||||
---
|
||||
|
||||
|
||||
# [Documentation](http://cocoadocs.org/docsets/FSPagerView)
|
||||
|
|
@ -14,6 +14,7 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
internal var leadingSpacing: CGFloat = 0
|
||||
internal var itemSpacing: CGFloat = 0
|
||||
internal var needsReprepare = true
|
||||
internal var scrollDirection: FSPagerViewScrollDirection = .horizontal
|
||||
|
||||
open override class var layoutAttributesClass: AnyClass {
|
||||
get {
|
||||
|
|
@ -82,17 +83,27 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
}
|
||||
return pagerView.interitemSpacing
|
||||
}()
|
||||
self.leadingSpacing = (collectionView.frame.width-self.actualItemSize.width)*0.5
|
||||
self.itemSpacing = self.actualItemSize.width+self.actualInteritemSpacing
|
||||
self.scrollDirection = pagerView.scrollDirection
|
||||
self.leadingSpacing = self.scrollDirection == .horizontal ? (collectionView.frame.width-self.actualItemSize.width)*0.5 : (collectionView.frame.height-self.actualItemSize.height)*0.5
|
||||
self.itemSpacing = (self.scrollDirection == .horizontal ? self.actualItemSize.width : self.actualItemSize.height) + self.actualInteritemSpacing
|
||||
|
||||
// Calculate and cache contentSize, rather than calculating each time
|
||||
self.contentSize = {
|
||||
let numberOfItems = self.numberOfItems*self.numberOfSections
|
||||
var contentSizeWidth: CGFloat = self.leadingSpacing*2 // Leading & trailing spacing
|
||||
contentSizeWidth += CGFloat(numberOfItems-1)*self.actualInteritemSpacing // Interitem spacing
|
||||
contentSizeWidth += CGFloat(numberOfItems)*self.actualItemSize.width // Item sizes
|
||||
let contentSize = CGSize(width: contentSizeWidth, height: collectionView.frame.height)
|
||||
return contentSize
|
||||
switch self.scrollDirection {
|
||||
case .horizontal:
|
||||
var contentSizeWidth: CGFloat = self.leadingSpacing*2 // Leading & trailing spacing
|
||||
contentSizeWidth += CGFloat(numberOfItems-1)*self.actualInteritemSpacing // Interitem spacing
|
||||
contentSizeWidth += CGFloat(numberOfItems)*self.actualItemSize.width // Item sizes
|
||||
let contentSize = CGSize(width: contentSizeWidth, height: collectionView.frame.height)
|
||||
return contentSize
|
||||
case .vertical:
|
||||
var contentSizeHeight: CGFloat = self.leadingSpacing*2 // Leading & trailing spacing
|
||||
contentSizeHeight += CGFloat(numberOfItems-1)*self.actualInteritemSpacing // Interitem spacing
|
||||
contentSizeHeight += CGFloat(numberOfItems)*self.actualItemSize.height // Item sizes
|
||||
let contentSize = CGSize(width: collectionView.frame.width, height: contentSizeHeight)
|
||||
return contentSize
|
||||
}
|
||||
}()
|
||||
self.adjustCollectionViewBounds()
|
||||
}
|
||||
|
|
@ -115,36 +126,36 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
return layoutAttributes
|
||||
}
|
||||
// Calculate start position and index of certain rects
|
||||
let numberOfItemsBefore = max(Int((rect.minX-self.leadingSpacing)/self.itemSpacing),0)
|
||||
let numberOfItemsBefore = self.scrollDirection == .horizontal ? max(Int((rect.minX-self.leadingSpacing)/self.itemSpacing),0) : max(Int((rect.minY-self.leadingSpacing)/self.itemSpacing),0)
|
||||
let startPosition = self.leadingSpacing + CGFloat(numberOfItemsBefore)*self.itemSpacing
|
||||
let startIndex = numberOfItemsBefore
|
||||
// Create layout attributes
|
||||
var itemIndex = startIndex
|
||||
var x = startPosition
|
||||
let maxPosition = min(rect.maxX,self.contentSize.width-self.actualItemSize.width-self.leadingSpacing)
|
||||
while x <= maxPosition {
|
||||
|
||||
var origin = startPosition
|
||||
let maxPosition = self.scrollDirection == .horizontal ? min(rect.maxX,self.contentSize.width-self.actualItemSize.width-self.leadingSpacing) : min(rect.maxY,self.contentSize.height-self.actualItemSize.height-self.leadingSpacing)
|
||||
while origin <= maxPosition {
|
||||
let indexPath = IndexPath(item: itemIndex%self.numberOfItems, section: itemIndex/self.numberOfItems)
|
||||
let attributes = self.layoutAttributesForItem(at: indexPath) as! FSPagerViewLayoutAttributes
|
||||
self.applyTransform(to: attributes)
|
||||
self.applyTransform(to: attributes, with: self.pagerView?.transformer)
|
||||
layoutAttributes.append(attributes)
|
||||
itemIndex += 1
|
||||
x += self.itemSpacing
|
||||
origin += self.itemSpacing
|
||||
}
|
||||
return layoutAttributes
|
||||
|
||||
}
|
||||
|
||||
override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
|
||||
guard let collectionView = self.collectionView else {
|
||||
return UICollectionViewLayoutAttributes(forCellWith: indexPath)
|
||||
var attributes = self.layoutAttributes[indexPath]
|
||||
if attributes == nil {
|
||||
attributes = FSPagerViewLayoutAttributes(forCellWith: indexPath)
|
||||
self.layoutAttributes[indexPath] = attributes
|
||||
}
|
||||
if let attributes = self.layoutAttributes[indexPath] {
|
||||
return attributes
|
||||
}
|
||||
let attributes = FSPagerViewLayoutAttributes(forCellWith: indexPath)
|
||||
let x = self.frame(for: indexPath).minX
|
||||
let center = CGPoint(x: x+self.actualItemSize.width*0.5, y: collectionView.frame.height*0.5)
|
||||
attributes.center = center
|
||||
attributes.size = self.actualItemSize
|
||||
let frame = self.frame(for: indexPath)
|
||||
let center = CGPoint(x: frame.midX, y: frame.midY)
|
||||
attributes!.center = center
|
||||
attributes!.size = self.actualItemSize
|
||||
self.layoutAttributes[indexPath] = attributes
|
||||
return attributes
|
||||
}
|
||||
|
|
@ -155,6 +166,9 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
}
|
||||
var proposedContentOffset = proposedContentOffset
|
||||
let proposedContentOffsetX: CGFloat = {
|
||||
if self.scrollDirection == .vertical {
|
||||
return proposedContentOffset.x
|
||||
}
|
||||
let translation = -collectionView.panGestureRecognizer.translation(in: collectionView).x
|
||||
var offset: CGFloat = round(proposedContentOffset.x/self.itemSpacing)*self.itemSpacing
|
||||
let minFlippingDistance = min(0.5 * self.itemSpacing,150)
|
||||
|
|
@ -166,7 +180,22 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
}
|
||||
return offset
|
||||
}()
|
||||
proposedContentOffset = CGPoint(x: proposedContentOffsetX, y: proposedContentOffset.y)
|
||||
let proposedContentOffsetY: CGFloat = {
|
||||
if self.scrollDirection == .horizontal {
|
||||
return proposedContentOffset.y
|
||||
}
|
||||
let translation = -collectionView.panGestureRecognizer.translation(in: collectionView).y
|
||||
var offset: CGFloat = round(proposedContentOffset.y/self.itemSpacing)*self.itemSpacing
|
||||
let minFlippingDistance = min(0.5 * self.itemSpacing,150)
|
||||
let originalContentOffsetY = collectionView.contentOffset.y - translation
|
||||
if abs(translation) <= minFlippingDistance {
|
||||
if abs(velocity.y) >= 0.3 && abs(proposedContentOffset.y-originalContentOffsetY) <= self.itemSpacing*0.5 {
|
||||
offset += self.itemSpacing * (velocity.y)/abs(velocity.y)
|
||||
}
|
||||
}
|
||||
return offset
|
||||
}()
|
||||
proposedContentOffset = CGPoint(x: proposedContentOffsetX, y: proposedContentOffsetY)
|
||||
return proposedContentOffset
|
||||
}
|
||||
|
||||
|
|
@ -182,14 +211,38 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
guard let collectionView = self.collectionView else {
|
||||
return origin
|
||||
}
|
||||
let contentOffset = CGPoint(x: origin.x - (collectionView.frame.width*0.5-self.actualItemSize.width*0.5), y: collectionView.contentOffset.y)
|
||||
let contentOffsetX: CGFloat = {
|
||||
if self.scrollDirection == .vertical {
|
||||
return 0
|
||||
}
|
||||
let contentOffsetX = origin.x - (collectionView.frame.width*0.5-self.actualItemSize.width*0.5)
|
||||
return contentOffsetX
|
||||
}()
|
||||
let contentOffsetY: CGFloat = {
|
||||
if self.scrollDirection == .horizontal {
|
||||
return 0
|
||||
}
|
||||
let contentOffsetY = origin.y - (collectionView.frame.height*0.5-self.actualItemSize.height*0.5)
|
||||
return contentOffsetY
|
||||
}()
|
||||
let contentOffset = CGPoint(x: contentOffsetX, y: contentOffsetY)
|
||||
return contentOffset
|
||||
}
|
||||
|
||||
internal func frame(for indexPath: IndexPath) -> CGRect {
|
||||
let numberOfItems = self.numberOfItems*indexPath.section + indexPath.item
|
||||
let originX = self.leadingSpacing + CGFloat(numberOfItems)*self.itemSpacing
|
||||
let originY = (self.collectionView!.frame.height-self.actualItemSize.height)/2.0
|
||||
let originX: CGFloat = {
|
||||
if self.scrollDirection == .vertical {
|
||||
return (self.collectionView!.frame.width-self.actualItemSize.width)*0.5
|
||||
}
|
||||
return self.leadingSpacing + CGFloat(numberOfItems)*self.itemSpacing
|
||||
}()
|
||||
let originY: CGFloat = {
|
||||
if self.scrollDirection == .horizontal {
|
||||
return (self.collectionView!.frame.height-self.actualItemSize.height)*0.5
|
||||
}
|
||||
return self.leadingSpacing + CGFloat(numberOfItems)*self.itemSpacing
|
||||
}()
|
||||
let origin = CGPoint(x: originX, y: originY)
|
||||
let frame = CGRect(origin: origin, size: self.actualItemSize)
|
||||
return frame
|
||||
|
|
@ -217,23 +270,27 @@ class FSPagerViewLayout: UICollectionViewLayout {
|
|||
}
|
||||
let currentIndex = pagerView.currentIndex
|
||||
let newIndexPath = IndexPath(item: currentIndex, section: self.isInfinite ? self.numberOfSections/2 : 0)
|
||||
let contentOffsetX = self.contentOffset(for: newIndexPath).x
|
||||
let contentOffset = CGPoint(x: contentOffsetX, y: 0)
|
||||
let contentOffset = self.contentOffset(for: newIndexPath)
|
||||
let newBounds = CGRect(origin: contentOffset, size: collectionView.frame.size)
|
||||
collectionView.bounds = newBounds
|
||||
}
|
||||
|
||||
fileprivate func applyTransform(to attributes: FSPagerViewLayoutAttributes) {
|
||||
fileprivate func applyTransform(to attributes: FSPagerViewLayoutAttributes, with transformer: FSPagerViewTransformer?) {
|
||||
guard let collectionView = self.collectionView else {
|
||||
return
|
||||
}
|
||||
guard let transformer = self.pagerView?.transformer else {
|
||||
guard let transformer = transformer else {
|
||||
return
|
||||
}
|
||||
let ruler = collectionView.bounds.midX
|
||||
attributes.center.x = self.frame(for: attributes.indexPath).midX
|
||||
attributes.position = (attributes.center.x-ruler)/self.itemSpacing
|
||||
attributes.interitemSpacing = self.actualInteritemSpacing
|
||||
switch self.scrollDirection {
|
||||
case .horizontal:
|
||||
let ruler = collectionView.bounds.midX
|
||||
attributes.position = (attributes.center.x-ruler)/self.itemSpacing
|
||||
case .vertical:
|
||||
let ruler = collectionView.bounds.midY
|
||||
attributes.position = (attributes.center.y-ruler)/self.itemSpacing
|
||||
}
|
||||
attributes.zIndex = Int(self.numberOfItems)-Int(attributes.position)
|
||||
transformer.applyTransform(to: attributes)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,15 +49,25 @@ open class FSPagerViewTransformer: NSObject {
|
|||
|
||||
// Apply transform to attributes - zIndex: Int, frame: CGRect, alpha: CGFloat, transform: CGAffineTransform or transform3D: CATransform3D.
|
||||
open func applyTransform(to attributes: FSPagerViewLayoutAttributes) {
|
||||
guard let pagerView = self.pagerView else {
|
||||
return
|
||||
}
|
||||
let position = attributes.position
|
||||
let itemSpacing = attributes.interitemSpacing + attributes.bounds.width
|
||||
let scrollDirection = pagerView.scrollDirection
|
||||
let itemSpacing = (scrollDirection == .horizontal ? attributes.bounds.width : attributes.bounds.height) + self.proposedInteritemSpacing()
|
||||
switch self.type {
|
||||
case .none:
|
||||
break
|
||||
case .crossFading:
|
||||
var zIndex = 0
|
||||
var alpha: CGFloat = 0
|
||||
let transform = CGAffineTransform(translationX: -itemSpacing * position, y: 0)
|
||||
var transform = CGAffineTransform.identity
|
||||
switch scrollDirection {
|
||||
case .horizontal:
|
||||
transform.tx = -itemSpacing * position
|
||||
case .vertical:
|
||||
transform.ty = -itemSpacing * position
|
||||
}
|
||||
if (abs(position) < 1) { // [-1,1]
|
||||
// Use the default slide transition when moving to the left page
|
||||
alpha = 1 - abs(position)
|
||||
|
|
@ -80,11 +90,18 @@ open class FSPagerViewTransformer: NSObject {
|
|||
case -1 ... 1 : // [-1,1]
|
||||
// Modify the default slide transition to shrink the page as well
|
||||
let scaleFactor = max(self.minimumScale, 1 - abs(position))
|
||||
let vertMargin = attributes.bounds.height * (1 - scaleFactor) / 2;
|
||||
let horzMargin = itemSpacing * (1 - scaleFactor) / 2;
|
||||
transform.a = scaleFactor
|
||||
transform.d = scaleFactor
|
||||
transform.tx = position < 0 ? (horzMargin - vertMargin*2) : (-horzMargin + vertMargin*2)
|
||||
switch scrollDirection {
|
||||
case .horizontal:
|
||||
let vertMargin = attributes.bounds.height * (1 - scaleFactor) / 2;
|
||||
let horzMargin = itemSpacing * (1 - scaleFactor) / 2;
|
||||
transform.tx = position < 0 ? (horzMargin - vertMargin*2) : (-horzMargin + vertMargin*2)
|
||||
case .vertical:
|
||||
let horzMargin = attributes.bounds.width * (1 - scaleFactor) / 2;
|
||||
let vertMargin = itemSpacing * (1 - scaleFactor) / 2;
|
||||
transform.ty = position < 0 ? (vertMargin - horzMargin*2) : (-vertMargin + horzMargin*2)
|
||||
}
|
||||
// Fade the page relative to its size.
|
||||
alpha = self.minimumAlpha + (scaleFactor-self.minimumScale)/(1-self.minimumScale)*(1-self.minimumAlpha)
|
||||
case 1 ... CGFloat.greatestFiniteMagnitude : // (1,+Infinity]
|
||||
|
|
@ -111,18 +128,23 @@ open class FSPagerViewTransformer: NSObject {
|
|||
transform.a = 1
|
||||
transform.d = 1
|
||||
zIndex = 1
|
||||
case 0 ..< 1: // (0,1]
|
||||
case 0 ..< 1: // (0,1)
|
||||
// Fade the page out.
|
||||
alpha = CGFloat(1.0) - position
|
||||
// Counteract the default slide transition
|
||||
transform.tx = itemSpacing * -position;
|
||||
switch scrollDirection {
|
||||
case .horizontal:
|
||||
transform.tx = itemSpacing * -position
|
||||
case .vertical:
|
||||
transform.ty = itemSpacing * -position
|
||||
}
|
||||
// Scale the page down (between minimumScale and 1)
|
||||
let scaleFactor = self.minimumScale
|
||||
+ (1.0 - self.minimumScale) * (1.0 - abs(position));
|
||||
transform.a = scaleFactor
|
||||
transform.d = scaleFactor
|
||||
zIndex = 0
|
||||
case 1 ... CGFloat.greatestFiniteMagnitude: // (1,+Infinity]
|
||||
case 1 ... CGFloat.greatestFiniteMagnitude: // [1,+Infinity)
|
||||
// This page is way off-screen to the right.
|
||||
alpha = 0
|
||||
zIndex = 0
|
||||
|
|
@ -133,6 +155,10 @@ open class FSPagerViewTransformer: NSObject {
|
|||
attributes.transform = transform
|
||||
attributes.zIndex = zIndex
|
||||
case .overlap,.linear:
|
||||
guard scrollDirection == .horizontal else {
|
||||
// This type doesn't support vertical mode
|
||||
return
|
||||
}
|
||||
let scale = max(1 - (1-self.minimumScale) * abs(position), self.minimumScale)
|
||||
let transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||
attributes.transform = transform
|
||||
|
|
@ -141,6 +167,10 @@ open class FSPagerViewTransformer: NSObject {
|
|||
let zIndex = (1-abs(position)) * 10
|
||||
attributes.zIndex = Int(zIndex)
|
||||
case .coverFlow:
|
||||
guard scrollDirection == .horizontal else {
|
||||
// This type doesn't support vertical mode
|
||||
return
|
||||
}
|
||||
let position = min(max(-position,-1) ,1)
|
||||
let rotation = sin(position*CGFloat(M_PI_2)) * CGFloat(M_PI_4)*1.5
|
||||
let translationZ = -itemSpacing * 0.5 * abs(position)
|
||||
|
|
@ -151,6 +181,10 @@ open class FSPagerViewTransformer: NSObject {
|
|||
attributes.zIndex = 100 - Int(abs(position))
|
||||
attributes.transform3D = transform3D
|
||||
case .ferrisWheel, .invertedFerrisWheel:
|
||||
guard scrollDirection == .horizontal else {
|
||||
// This type doesn't support vertical mode
|
||||
return
|
||||
}
|
||||
// http://ronnqvi.st/translate-rotate-translate/
|
||||
var zIndex = 0
|
||||
var transform = CGAffineTransform.identity
|
||||
|
|
@ -175,22 +209,34 @@ open class FSPagerViewTransformer: NSObject {
|
|||
attributes.zIndex = zIndex
|
||||
case .cubic:
|
||||
switch position {
|
||||
case -1...1:
|
||||
case -CGFloat.greatestFiniteMagnitude ... -1:
|
||||
attributes.alpha = 0
|
||||
case -1 ..< 1:
|
||||
attributes.alpha = 1
|
||||
attributes.zIndex = Int((1-position) * CGFloat(10))
|
||||
let direction: CGFloat = position < 0 ? 1 : -1
|
||||
let theta = position * CGFloat(M_PI_2)
|
||||
let width = attributes.bounds.width
|
||||
// ForwardX -> RotateY -> BackwardX
|
||||
attributes.center.x += direction*width*0.5 // ForwardX
|
||||
let theta = position * CGFloat(M_PI_2) * (scrollDirection == .horizontal ? 1 : -1)
|
||||
let radius = scrollDirection == .horizontal ? attributes.bounds.width : attributes.bounds.height
|
||||
var transform3D = CATransform3DIdentity
|
||||
transform3D.m34 = -0.002
|
||||
transform3D = CATransform3DRotate(transform3D, theta, 0, 1, 0) // RotateY
|
||||
transform3D = CATransform3DTranslate(transform3D,-direction*width*0.5, 0, 0) // BackwardX
|
||||
switch scrollDirection {
|
||||
case .horizontal:
|
||||
// ForwardX -> RotateY -> BackwardX
|
||||
attributes.center.x += direction*radius*0.5 // ForwardX
|
||||
transform3D = CATransform3DRotate(transform3D, theta, 0, 1, 0) // RotateY
|
||||
transform3D = CATransform3DTranslate(transform3D,-direction*radius*0.5, 0, 0) // BackwardX
|
||||
case .vertical:
|
||||
// ForwardY -> RotateX -> BackwardY
|
||||
attributes.center.y += direction*radius*0.5 // ForwardY
|
||||
transform3D = CATransform3DRotate(transform3D, theta, 1, 0, 0) // RotateX
|
||||
transform3D = CATransform3DTranslate(transform3D,0, -direction*radius*0.5, 0) // BackwardY
|
||||
}
|
||||
attributes.transform3D = transform3D
|
||||
default:
|
||||
attributes.zIndex = 0
|
||||
case 1 ... CGFloat.greatestFiniteMagnitude:
|
||||
attributes.alpha = 0
|
||||
default:
|
||||
attributes.alpha = 0
|
||||
attributes.zIndex = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -200,14 +246,27 @@ open class FSPagerViewTransformer: NSObject {
|
|||
guard let pagerView = self.pagerView else {
|
||||
return 0
|
||||
}
|
||||
let scrollDirection = pagerView.scrollDirection
|
||||
switch self.type {
|
||||
case .overlap:
|
||||
guard scrollDirection == .horizontal else {
|
||||
return 0
|
||||
}
|
||||
return pagerView.itemSize.width * -self.minimumScale * 0.6
|
||||
case .linear:
|
||||
guard scrollDirection == .horizontal else {
|
||||
return 0
|
||||
}
|
||||
return pagerView.itemSize.width * -self.minimumScale * 0.2
|
||||
case .coverFlow:
|
||||
guard scrollDirection == .horizontal else {
|
||||
return 0
|
||||
}
|
||||
return -pagerView.itemSize.width * sin(CGFloat(M_PI_4)/4.0*3.0)
|
||||
case .ferrisWheel,.invertedFerrisWheel:
|
||||
guard scrollDirection == .horizontal else {
|
||||
return 0
|
||||
}
|
||||
return -pagerView.itemSize.width * 0.15
|
||||
case .cubic:
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import UIKit
|
|||
|
||||
class FSPagerViewCollectionView: UICollectionView {
|
||||
|
||||
fileprivate weak var pagerView: FSPagerView? {
|
||||
fileprivate var pagerView: FSPagerView? {
|
||||
return self.superview?.superview as? FSPagerView
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,12 @@ public protocol FSPagerViewDelegate: NSObjectProtocol {
|
|||
|
||||
}
|
||||
|
||||
@objc
|
||||
public enum FSPagerViewScrollDirection: Int {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
|
||||
@IBDesignable
|
||||
open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelegate {
|
||||
|
||||
|
|
@ -87,7 +93,14 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
open weak var dataSource: FSPagerViewDataSource?
|
||||
open weak var delegate: FSPagerViewDelegate?
|
||||
#endif
|
||||
|
||||
|
||||
/// The scroll direction of the pager view. Default is horizontal.
|
||||
open var scrollDirection: FSPagerViewScrollDirection = .horizontal {
|
||||
didSet {
|
||||
self.collectionViewLayout.forceInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/// The time interval of automatic sliding. 0 means disabling automatic sliding. Default is 0.
|
||||
@IBInspectable
|
||||
open var automaticSlidingInterval: CGFloat = 0.0 {
|
||||
|
|
@ -119,8 +132,8 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
@IBInspectable
|
||||
open var isInfinite: Bool = false {
|
||||
didSet {
|
||||
self.collectionViewLayout.needsReprepare = true
|
||||
self.collectionView.reloadData()
|
||||
self.collectionViewLayout.forceInvalidate()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +169,8 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
|
||||
/// The percentage of x position at which the origin of the content view is offset from the origin of the pagerView view.
|
||||
open var scrollOffset: CGFloat {
|
||||
let scrollOffset = Double(self.collectionView.contentOffset.x.divided(by: self.collectionViewLayout.itemSpacing))
|
||||
let contentOffset = max(self.collectionView.contentOffset.x, self.collectionView.contentOffset.y)
|
||||
let scrollOffset = Double(contentOffset.divided(by: self.collectionViewLayout.itemSpacing))
|
||||
return fmod(CGFloat(scrollOffset), CGFloat(Double(self.numberOfItems)))
|
||||
}
|
||||
|
||||
|
|
@ -179,13 +193,23 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
|
||||
fileprivate var dequeingSection = 0
|
||||
fileprivate var centermostIndexPath: IndexPath {
|
||||
guard self.numberOfItems > 0, self.collectionView.contentSize.width > 0 else {
|
||||
guard self.numberOfItems > 0, self.collectionView.contentSize != .zero else {
|
||||
return IndexPath(item: 0, section: 0)
|
||||
}
|
||||
let sortedIndexPaths = self.collectionView.indexPathsForVisibleItems.sorted { (l, r) -> Bool in
|
||||
let leftCenter = self.collectionViewLayout.frame(for: l).midX
|
||||
let rightCenter = self.collectionViewLayout.frame(for: r).midX
|
||||
let ruler = self.collectionView.bounds.midX
|
||||
let leftFrame = self.collectionViewLayout.frame(for: l)
|
||||
let rightFrame = self.collectionViewLayout.frame(for: r)
|
||||
var leftCenter: CGFloat,rightCenter: CGFloat,ruler: CGFloat
|
||||
switch self.scrollDirection {
|
||||
case .horizontal:
|
||||
leftCenter = leftFrame.midX
|
||||
rightCenter = rightFrame.midX
|
||||
ruler = self.collectionView.bounds.midX
|
||||
case .vertical:
|
||||
leftCenter = leftFrame.midY
|
||||
rightCenter = rightFrame.midY
|
||||
ruler = self.collectionView.bounds.midY
|
||||
}
|
||||
return abs(ruler-leftCenter) < abs(ruler-rightCenter)
|
||||
}
|
||||
let indexPath = sortedIndexPaths.first
|
||||
|
|
@ -344,7 +368,8 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
|
||||
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
if let function = self.delegate?.pagerViewWillEndDragging(_:targetIndex:) {
|
||||
let targetItem = lround(Double(targetContentOffset.pointee.x/self.collectionViewLayout.itemSpacing))
|
||||
let contentOffset = self.scrollDirection == .horizontal ? targetContentOffset.pointee.x : targetContentOffset.pointee.y
|
||||
let targetItem = lround(Double(contentOffset/self.collectionViewLayout.itemSpacing))
|
||||
function(self, targetItem % self.numberOfItems)
|
||||
}
|
||||
if self.automaticSlidingInterval > 0 {
|
||||
|
|
@ -417,7 +442,8 @@ open class FSPagerView: UIView,UICollectionViewDataSource,UICollectionViewDelega
|
|||
@objc(selectItemAtIndex:animated:)
|
||||
open func selectItem(at index: Int, animated: Bool) {
|
||||
let indexPath = self.nearbyIndexPath(for: index)
|
||||
self.collectionView.selectItem(at: indexPath, animated: animated, scrollPosition: .centeredHorizontally)
|
||||
let scrollPosition: UICollectionViewScrollPosition = self.scrollDirection == .horizontal ? .centeredVertically : .centeredVertically
|
||||
self.collectionView.selectItem(at: indexPath, animated: animated, scrollPosition: scrollPosition)
|
||||
}
|
||||
|
||||
/// Deselects the item at the specified index.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import UIKit
|
|||
open class FSPagerViewLayoutAttributes: UICollectionViewLayoutAttributes {
|
||||
|
||||
open var position: CGFloat = 0
|
||||
open var interitemSpacing: CGFloat = 0
|
||||
|
||||
open override func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? FSPagerViewLayoutAttributes else {
|
||||
|
|
@ -19,14 +18,12 @@ open class FSPagerViewLayoutAttributes: UICollectionViewLayoutAttributes {
|
|||
}
|
||||
var isEqual = super.isEqual(object)
|
||||
isEqual = isEqual && (self.position == object.position)
|
||||
isEqual = isEqual && (self.interitemSpacing == object.interitemSpacing)
|
||||
return isEqual
|
||||
}
|
||||
|
||||
open override func copy(with zone: NSZone? = nil) -> Any {
|
||||
let copy = super.copy(with: zone) as! FSPagerViewLayoutAttributes
|
||||
copy.position = self.position
|
||||
copy.interitemSpacing = self.interitemSpacing
|
||||
return copy
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue