二手车信息

This commit is contained in:
ddisfriend
2025-03-13 15:09:17 +08:00
parent 59dbd0af52
commit 81b09a20f8
44 changed files with 9891 additions and 7645 deletions

21
Pods/TYCyclePagerView/LICENSE generated Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 yeBlueColor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

141
Pods/TYCyclePagerView/README.md generated Executable file
View File

@@ -0,0 +1,141 @@
# TYCyclePagerView
a simple and usefull cycle pager view ,and auto scroll banner view ,include pageControl for iOS,support Objective-C and swift.this has been used in APP.
## CocoaPods
```
pod 'TYCyclePagerView'
```
## Requirements
* Xcode 8 or higher
* iOS 7.0 or higher
* ARC
### ScreenShot
![image](https://github.com/12207480/TYCyclePagerView/blob/master/ScreenShot/TYCyclePagerView.gif)
## API
* DataSource and Delegate
```objc
@protocol TYCyclePagerViewDataSource <NSObject>
- (NSInteger)numberOfItemsInPagerView:(TYCyclePagerView *)pageView;
- (__kindof UICollectionViewCell *)pagerView:(TYCyclePagerView *)pagerView cellForItemAtIndex:(NSInteger)index;
/**
return pagerView layout,and cache layout
*/
- (TYCyclePagerViewLayout *)layoutForPagerView:(TYCyclePagerView *)pageView;
@protocol TYCyclePagerViewDelegate <NSObject>
@optional
/**
pagerView did scroll to new index page
*/
- (void)pagerView:(TYCyclePagerView *)pageView didScrollFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex;
/**
pagerView did selected item cell
*/
- (void)pagerView:(TYCyclePagerView *)pageView didSelectedItemCell:(__kindof UICollectionViewCell *)cell atIndex:(NSInteger)index;
// More API see project
```
* Class
```objc
@interface TYCyclePagerView : UIView
// will be automatically resized to track the size of the pagerView
@property (nonatomic, strong, nullable) UIView *backgroundView;
@property (nonatomic, weak, nullable) id<TYCyclePagerViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id<TYCyclePagerViewDelegate> delegate;
// pager view layout is important
@property (nonatomic, strong, readonly) TYCyclePagerViewLayout *layout;
/**
is infinite cycle pageview
*/
@property (nonatomic, assign) BOOL isInfiniteLoop;
/**
pagerView automatic scroll time interval, default 0,disable automatic
*/
@property (nonatomic, assign) CGFloat autoScrollInterval;
@interface TYCyclePagerViewLayout : NSObject
@property (nonatomic, assign) CGSize itemSize;
@property (nonatomic, assign) CGFloat itemSpacing;
@property (nonatomic, assign) UIEdgeInsets sectionInset;
@property (nonatomic, assign) TYCyclePagerTransformLayoutType layoutType;
@property (nonatomic, assign) CGFloat minimumScale; // sacle default 0.8
@property (nonatomic, assign) CGFloat minimumAlpha; // alpha default 1.0
@property (nonatomic, assign) CGFloat maximumAngle; // angle is % default 0.2
@interface TYPageControl : UIControl
@property (nonatomic, assign) NSInteger numberOfPages; // default is 0
@property (nonatomic, assign) NSInteger currentPage; // default is 0. value pinned to 0..numberOfPages-1
// indicatorTint color
@property (nullable, nonatomic,strong) UIColor *pageIndicatorTintColor;
@property (nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor;
// indicator image
@property (nullable, nonatomic,strong) UIImage *pageIndicatorImage;
@property (nullable, nonatomic,strong) UIImage *currentPageIndicatorImage;
```
## Usage
```objc
- (void)addPagerView {
TYCyclePagerView *pagerView = [[TYCyclePagerView alloc]init];
pagerView.layer.borderWidth = 1;
pagerView.isInfiniteLoop = YES;
pagerView.autoScrollInterval = 3.0;
pagerView.dataSource = self;
pagerView.delegate = self;
// registerClass or registerNib
[pagerView registerClass:[TYCyclePagerViewCell class] forCellWithReuseIdentifier:@"cellId"];
[self.view addSubview:pagerView];
_pagerView = pagerView;
}
- (void)addPageControl {
TYPageControl *pageControl = [[TYPageControl alloc]init];
//pageControl.numberOfPages = _datas.count;
pageControl.currentPageIndicatorSize = CGSizeMake(8, 8);
// pageControl.pageIndicatorImage = [UIImage imageNamed:@"Dot"];
// pageControl.currentPageIndicatorImage = [UIImage imageNamed:@"DotSelected"];
// [pageControl addTarget:self action:@selector(pageControlValueChangeAction:) forControlEvents:UIControlEventValueChanged];
[_pagerView addSubview:pageControl];
_pageControl = pageControl;
}
- (void)loadData {
// load data to _datas
_pageControl.numberOfPages = _datas.count;
[_pagerView reloadData];
}
```
### Contact
如果你发现bugplease pull reqeust me <br>
如果你有更好的改进please pull reqeust me <br>

View File

@@ -0,0 +1,74 @@
//
// TYCyclePagerViewLayout.h
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/19.
// Copyright © 2017年 tany. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, TYCyclePagerTransformLayoutType) {
TYCyclePagerTransformLayoutNormal,
TYCyclePagerTransformLayoutLinear,
TYCyclePagerTransformLayoutCoverflow,
};
@class TYCyclePagerTransformLayout;
@protocol TYCyclePagerTransformLayoutDelegate <NSObject>
// initialize layout attributes
- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes;
// apply layout attributes
- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes;
@end
@interface TYCyclePagerViewLayout : NSObject
@property (nonatomic, assign) CGSize itemSize;
@property (nonatomic, assign) CGFloat itemSpacing;
@property (nonatomic, assign) UIEdgeInsets sectionInset;
@property (nonatomic, assign) TYCyclePagerTransformLayoutType layoutType;
@property (nonatomic, assign) CGFloat minimumScale; // sacle default 0.8
@property (nonatomic, assign) CGFloat minimumAlpha; // alpha default 1.0
@property (nonatomic, assign) CGFloat maximumAngle; // angle is % default 0.2
@property (nonatomic, assign) BOOL isInfiniteLoop; // infinte scroll
@property (nonatomic, assign) CGFloat rateOfChange; // scale and angle change rate
@property (nonatomic, assign) BOOL adjustSpacingWhenScroling;
/**
pageView cell item vertical centering
*/
@property (nonatomic, assign) BOOL itemVerticalCenter;
/**
first and last item horizontalc enter, when isInfiniteLoop is NO
*/
@property (nonatomic, assign) BOOL itemHorizontalCenter;
// sectionInset
@property (nonatomic, assign, readonly) UIEdgeInsets onlyOneSectionInset;
@property (nonatomic, assign, readonly) UIEdgeInsets firstSectionInset;
@property (nonatomic, assign, readonly) UIEdgeInsets lastSectionInset;
@property (nonatomic, assign, readonly) UIEdgeInsets middleSectionInset;
@end
@interface TYCyclePagerTransformLayout : UICollectionViewFlowLayout
@property (nonatomic, strong) TYCyclePagerViewLayout *layout;
@property (nonatomic, weak, nullable) id<TYCyclePagerTransformLayoutDelegate> delegate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,300 @@
//
// TYCyclePagerViewLayout.m
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/19.
// Copyright © 2017 tany. All rights reserved.
//
#import "TYCyclePagerTransformLayout.h"
typedef NS_ENUM(NSUInteger, TYTransformLayoutItemDirection) {
TYTransformLayoutItemLeft,
TYTransformLayoutItemCenter,
TYTransformLayoutItemRight,
};
@interface TYCyclePagerTransformLayout () {
struct {
unsigned int applyTransformToAttributes :1;
unsigned int initializeTransformAttributes :1;
}_delegateFlags;
}
@property (nonatomic, assign) BOOL applyTransformToAttributesDelegate;
@end
@interface TYCyclePagerViewLayout ()
@property (nonatomic, weak) UIView *pageView;
@end
@implementation TYCyclePagerTransformLayout
- (instancetype)init {
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
#pragma mark - getter setter
- (void)setDelegate:(id<TYCyclePagerTransformLayoutDelegate>)delegate {
_delegate = delegate;
_delegateFlags.initializeTransformAttributes = [delegate respondsToSelector:@selector(pagerViewTransformLayout:initializeTransformAttributes:)];
_delegateFlags.applyTransformToAttributes = [delegate respondsToSelector:@selector(pagerViewTransformLayout:applyTransformToAttributes:)];
}
- (void)setLayout:(TYCyclePagerViewLayout *)layout {
_layout = layout;
_layout.pageView = self.collectionView;
self.itemSize = _layout.itemSize;
self.minimumInteritemSpacing = _layout.itemSpacing;
self.minimumLineSpacing = _layout.itemSpacing;
}
- (CGSize)itemSize {
if (!_layout) {
return [super itemSize];
}
return _layout.itemSize;
}
- (CGFloat)minimumLineSpacing {
if (!_layout) {
return [super minimumLineSpacing];
}
return _layout.itemSpacing;
}
- (CGFloat)minimumInteritemSpacing {
if (!_layout) {
return [super minimumInteritemSpacing];
}
return _layout.itemSpacing;
}
- (TYTransformLayoutItemDirection)directionWithCenterX:(CGFloat)centerX {
TYTransformLayoutItemDirection direction= TYTransformLayoutItemRight;
CGFloat contentCenterX = self.collectionView.contentOffset.x + CGRectGetWidth(self.collectionView.frame)/2;
if (ABS(centerX - contentCenterX) < 0.5) {
direction = TYTransformLayoutItemCenter;
}else if (centerX - contentCenterX < 0) {
direction = TYTransformLayoutItemLeft;
}
return direction;
}
#pragma mark - layout
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
{
return _layout.layoutType == TYCyclePagerTransformLayoutNormal ? [super shouldInvalidateLayoutForBoundsChange:newBounds] : YES;
}
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
if (_delegateFlags.applyTransformToAttributes || _layout.layoutType != TYCyclePagerTransformLayoutNormal) {
NSArray *attributesArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
CGRect visibleRect = {self.collectionView.contentOffset,self.collectionView.bounds.size};
for (UICollectionViewLayoutAttributes *attributes in attributesArray) {
if (!CGRectIntersectsRect(visibleRect, attributes.frame)) {
continue;
}
if (_delegateFlags.applyTransformToAttributes) {
[_delegate pagerViewTransformLayout:self applyTransformToAttributes:attributes];
}else {
[self applyTransformToAttributes:attributes layoutType:_layout.layoutType];
}
}
return attributesArray;
}
return [super layoutAttributesForElementsInRect:rect];
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *attributes = [super layoutAttributesForItemAtIndexPath:indexPath];
if (_delegateFlags.initializeTransformAttributes) {
[_delegate pagerViewTransformLayout:self initializeTransformAttributes:attributes];
}else if(_layout.layoutType != TYCyclePagerTransformLayoutNormal){
[self initializeTransformAttributes:attributes layoutType:_layout.layoutType];
}
return attributes;
}
#pragma mark - transform
- (void)initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes layoutType:(TYCyclePagerTransformLayoutType)layoutType {
switch (layoutType) {
case TYCyclePagerTransformLayoutLinear:
[self applyLinearTransformToAttributes:attributes scale:_layout.minimumScale alpha:_layout.minimumAlpha];
break;
case TYCyclePagerTransformLayoutCoverflow:
{
[self applyCoverflowTransformToAttributes:attributes angle:_layout.maximumAngle alpha:_layout.minimumAlpha];
break;
}
default:
break;
}
}
- (void)applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes layoutType:(TYCyclePagerTransformLayoutType)layoutType {
switch (layoutType) {
case TYCyclePagerTransformLayoutLinear:
[self applyLinearTransformToAttributes:attributes];
break;
case TYCyclePagerTransformLayoutCoverflow:
[self applyCoverflowTransformToAttributes:attributes];
break;
default:
break;
}
}
#pragma mark - LinearTransform
- (void)applyLinearTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes {
CGFloat collectionViewWidth = self.collectionView.frame.size.width;
if (collectionViewWidth <= 0) {
return;
}
CGFloat centetX = self.collectionView.contentOffset.x + collectionViewWidth/2;
CGFloat delta = ABS(attributes.center.x - centetX);
CGFloat scale = MAX(1 - delta/collectionViewWidth*_layout.rateOfChange, _layout.minimumScale);
CGFloat alpha = MAX(1 - delta/collectionViewWidth, _layout.minimumAlpha);
[self applyLinearTransformToAttributes:attributes scale:scale alpha:alpha];
}
- (void)applyLinearTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes scale:(CGFloat)scale alpha:(CGFloat)alpha {
CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
if (_layout.adjustSpacingWhenScroling) {
TYTransformLayoutItemDirection direction = [self directionWithCenterX:attributes.center.x];
CGFloat translate = 0;
switch (direction) {
case TYTransformLayoutItemLeft:
translate = 1.15 * attributes.size.width*(1-scale)/2;
break;
case TYTransformLayoutItemRight:
translate = -1.15 * attributes.size.width*(1-scale)/2;
break;
default:
// center
scale = 1.0;
alpha = 1.0;
break;
}
transform = CGAffineTransformTranslate(transform,translate, 0);
}
attributes.transform = transform;
attributes.alpha = alpha;
}
#pragma mark - CoverflowTransform
- (void)applyCoverflowTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes{
CGFloat collectionViewWidth = self.collectionView.frame.size.width;
if (collectionViewWidth <= 0) {
return;
}
CGFloat centetX = self.collectionView.contentOffset.x + collectionViewWidth/2;
CGFloat delta = ABS(attributes.center.x - centetX);
CGFloat angle = MIN(delta/collectionViewWidth*(1-_layout.rateOfChange), _layout.maximumAngle);
CGFloat alpha = MAX(1 - delta/collectionViewWidth, _layout.minimumAlpha);
[self applyCoverflowTransformToAttributes:attributes angle:angle alpha:alpha];
}
- (void)applyCoverflowTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes angle:(CGFloat)angle alpha:(CGFloat)alpha {
TYTransformLayoutItemDirection direction = [self directionWithCenterX:attributes.center.x];
CATransform3D transform3D = CATransform3DIdentity;
transform3D.m34 = -0.002;
CGFloat translate = 0;
switch (direction) {
case TYTransformLayoutItemLeft:
translate = (1-cos(angle*1.2*M_PI))*attributes.size.width;
break;
case TYTransformLayoutItemRight:
translate = -(1-cos(angle*1.2*M_PI))*attributes.size.width;
angle = -angle;
break;
default:
// center
angle = 0;
alpha = 1;
break;
}
transform3D = CATransform3DRotate(transform3D, M_PI*angle, 0, 1, 0);
if (_layout.adjustSpacingWhenScroling) {
transform3D = CATransform3DTranslate(transform3D, translate, 0, 0);
}
attributes.transform3D = transform3D;
attributes.alpha = alpha;
}
@end
@implementation TYCyclePagerViewLayout
- (instancetype)init {
if (self = [super init]) {
_itemVerticalCenter = YES;
_minimumScale = 0.8;
_minimumAlpha = 1.0;
_maximumAngle = 0.2;
_rateOfChange = 0.4;
_adjustSpacingWhenScroling = YES;
}
return self;
}
#pragma mark - getter
- (UIEdgeInsets)onlyOneSectionInset {
CGFloat leftSpace = _pageView && !_isInfiniteLoop && _itemHorizontalCenter ? (CGRectGetWidth(_pageView.frame) - _itemSize.width)/2 : _sectionInset.left;
CGFloat rightSpace = _pageView && !_isInfiniteLoop && _itemHorizontalCenter ? (CGRectGetWidth(_pageView.frame) - _itemSize.width)/2 : _sectionInset.right;
if (_itemVerticalCenter) {
CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
return UIEdgeInsetsMake(verticalSpace, leftSpace, verticalSpace, rightSpace);
}
return UIEdgeInsetsMake(_sectionInset.top, leftSpace, _sectionInset.bottom, rightSpace);
}
- (UIEdgeInsets)firstSectionInset {
if (_itemVerticalCenter) {
CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
return UIEdgeInsetsMake(verticalSpace, _sectionInset.left, verticalSpace, _itemSpacing);
}
return UIEdgeInsetsMake(_sectionInset.top, _sectionInset.left, _sectionInset.bottom, _itemSpacing);
}
- (UIEdgeInsets)lastSectionInset {
if (_itemVerticalCenter) {
CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
return UIEdgeInsetsMake(verticalSpace, 0, verticalSpace, _sectionInset.right);
}
return UIEdgeInsetsMake(_sectionInset.top, 0, _sectionInset.bottom, _sectionInset.right);
}
- (UIEdgeInsets)middleSectionInset {
if (_itemVerticalCenter) {
CGFloat verticalSpace = (CGRectGetHeight(_pageView.frame) - _itemSize.height)/2;
return UIEdgeInsetsMake(verticalSpace, 0, verticalSpace, _itemSpacing);
}
return _sectionInset;
}
@end

View File

@@ -0,0 +1,180 @@
//
// TYCyclePagerView.h
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/14.
// Copyright © 2017年 tany. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TYCyclePagerTransformLayout.h"
NS_ASSUME_NONNULL_BEGIN
typedef struct {
NSInteger index;
NSInteger section;
}TYIndexSection;
// pagerView scrolling direction
typedef NS_ENUM(NSUInteger, TYPagerScrollDirection) {
TYPagerScrollDirectionLeft,
TYPagerScrollDirectionRight,
};
@class TYCyclePagerView;
@protocol TYCyclePagerViewDataSource <NSObject>
- (NSInteger)numberOfItemsInPagerView:(TYCyclePagerView *)pageView;
- (__kindof UICollectionViewCell *)pagerView:(TYCyclePagerView *)pagerView cellForItemAtIndex:(NSInteger)index;
/**
return pagerView layout,and cache layout
*/
- (TYCyclePagerViewLayout *)layoutForPagerView:(TYCyclePagerView *)pageView;
@end
@protocol TYCyclePagerViewDelegate <NSObject>
@optional
/**
pagerView did scroll to new index page
*/
- (void)pagerView:(TYCyclePagerView *)pageView didScrollFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex;
/**
pagerView did selected item cell
*/
- (void)pagerView:(TYCyclePagerView *)pageView didSelectedItemCell:(__kindof UICollectionViewCell *)cell atIndex:(NSInteger)index;
- (void)pagerView:(TYCyclePagerView *)pageView didSelectedItemCell:(__kindof UICollectionViewCell *)cell atIndexSection:(TYIndexSection)indexSection;
// custom layout
- (void)pagerView:(TYCyclePagerView *)pageView initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes;
- (void)pagerView:(TYCyclePagerView *)pageView applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes;
// scrollViewDelegate
- (void)pagerViewDidScroll:(TYCyclePagerView *)pageView;
- (void)pagerViewWillBeginDragging:(TYCyclePagerView *)pageView;
- (void)pagerViewDidEndDragging:(TYCyclePagerView *)pageView willDecelerate:(BOOL)decelerate;
- (void)pagerViewWillBeginDecelerating:(TYCyclePagerView *)pageView;
- (void)pagerViewDidEndDecelerating:(TYCyclePagerView *)pageView;
- (void)pagerViewWillBeginScrollingAnimation:(TYCyclePagerView *)pageView;
- (void)pagerViewDidEndScrollingAnimation:(TYCyclePagerView *)pageView;
@end
@interface TYCyclePagerView : UIView
// will be automatically resized to track the size of the pagerView
@property (nonatomic, strong, nullable) UIView *backgroundView;
@property (nonatomic, weak, nullable) id<TYCyclePagerViewDataSource> dataSource;
@property (nonatomic, weak, nullable) id<TYCyclePagerViewDelegate> delegate;
// pager view, don't set dataSource and delegate
@property (nonatomic, weak, readonly) UICollectionView *collectionView;
// pager view layout
@property (nonatomic, strong, readonly) TYCyclePagerViewLayout *layout;
/**
is infinite cycle pageview
*/
@property (nonatomic, assign) BOOL isInfiniteLoop;
/**
pagerView automatic scroll time interval, default 0,disable automatic
*/
@property (nonatomic, assign) CGFloat autoScrollInterval;
@property (nonatomic, assign) BOOL reloadDataNeedResetIndex;
/**
current page index
*/
@property (nonatomic, assign, readonly) NSInteger curIndex;
@property (nonatomic, assign, readonly) TYIndexSection indexSection;
// scrollView property
@property (nonatomic, assign, readonly) CGPoint contentOffset;
@property (nonatomic, assign, readonly) BOOL tracking;
@property (nonatomic, assign, readonly) BOOL dragging;
@property (nonatomic, assign, readonly) BOOL decelerating;
/**
reload data, !!important!!: will clear layout and call delegate layoutForPagerView
*/
- (void)reloadData;
/**
update data is reload data, but not clear layuot
*/
- (void)updateData;
/**
if you only want update layout
*/
- (void)setNeedUpdateLayout;
/**
will set layout nil and call delegate->layoutForPagerView
*/
- (void)setNeedClearLayout;
/**
current index cell in pagerView
*/
- (__kindof UICollectionViewCell * _Nullable)curIndexCell;
/**
visible cells in pageView
*/
- (NSArray<__kindof UICollectionViewCell *> *_Nullable)visibleCells;
/**
visible pageView indexs, maybe repeat index
*/
- (NSArray *)visibleIndexs;
/**
scroll to item at index
*/
- (void)scrollToItemAtIndex:(NSInteger)index animate:(BOOL)animate;
- (void)scrollToItemAtIndexSection:(TYIndexSection)indexSection animate:(BOOL)animate;
/**
scroll to next or pre item
*/
- (void)scrollToNearlyIndexAtDirection:(TYPagerScrollDirection)direction animate:(BOOL)animate;
/**
register pager view cell with class
*/
- (void)registerClass:(Class)Class forCellWithReuseIdentifier:(NSString *)identifier;
/**
register pager view cell with nib
*/
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
/**
dequeue reusable cell for pagerView
*/
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,607 @@
//
// TYCyclePagerView.m
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/14.
// Copyright © 2017 tany. All rights reserved.
//
#import "TYCyclePagerView.h"
NS_INLINE BOOL TYEqualIndexSection(TYIndexSection indexSection1,TYIndexSection indexSection2) {
return indexSection1.index == indexSection2.index && indexSection1.section == indexSection2.section;
}
NS_INLINE TYIndexSection TYMakeIndexSection(NSInteger index, NSInteger section) {
TYIndexSection indexSection;
indexSection.index = index;
indexSection.section = section;
return indexSection;
}
@interface TYCyclePagerView () <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, TYCyclePagerTransformLayoutDelegate> {
struct {
unsigned int pagerViewDidScroll :1;
unsigned int didScrollFromIndexToNewIndex :1;
unsigned int initializeTransformAttributes :1;
unsigned int applyTransformToAttributes :1;
}_delegateFlags;
struct {
unsigned int cellForItemAtIndex :1;
unsigned int layoutForPagerView :1;
}_dataSourceFlags;
}
// UI
@property (nonatomic, weak) UICollectionView *collectionView;
@property (nonatomic, strong) TYCyclePagerViewLayout *layout;
@property (nonatomic, strong) NSTimer *timer;
// Data
@property (nonatomic, assign) NSInteger numberOfItems;
@property (nonatomic, assign) NSInteger dequeueSection;
@property (nonatomic, assign) TYIndexSection beginDragIndexSection;
@property (nonatomic, assign) NSInteger firstScrollIndex;
@property (nonatomic, assign) BOOL needClearLayout;
@property (nonatomic, assign) BOOL didReloadData;
@property (nonatomic, assign) BOOL didLayout;
@property (nonatomic, assign) BOOL needResetIndex;
@end
#define kPagerViewMaxSectionCount 200
#define kPagerViewMinSectionCount 18
@implementation TYCyclePagerView
#pragma mark - life Cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self configureProperty];
[self addCollectionView];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self configureProperty];
[self addCollectionView];
}
return self;
}
- (void)configureProperty {
_needResetIndex = NO;
_didReloadData = NO;
_didLayout = NO;
_autoScrollInterval = 0;
_isInfiniteLoop = YES;
_beginDragIndexSection.index = 0;
_beginDragIndexSection.section = 0;
_indexSection.index = -1;
_indexSection.section = -1;
_firstScrollIndex = -1;
}
- (void)addCollectionView {
TYCyclePagerTransformLayout *layout = [[TYCyclePagerTransformLayout alloc]init];
UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
layout.delegate = _delegateFlags.applyTransformToAttributes ? self : nil;;
collectionView.backgroundColor = [UIColor clearColor];
collectionView.dataSource = self;
collectionView.delegate = self;
collectionView.pagingEnabled = NO;
collectionView.decelerationRate = 1-0.0076;
if ([collectionView respondsToSelector:@selector(setPrefetchingEnabled:)]) {
collectionView.prefetchingEnabled = NO;
}
collectionView.showsHorizontalScrollIndicator = NO;
collectionView.showsVerticalScrollIndicator = NO;
[self addSubview:collectionView];
_collectionView = collectionView;
}
- (void)willMoveToSuperview:(UIView *)newSuperview {
if (!newSuperview) {
[self removeTimer];
}else {
[self removeTimer];
if (_autoScrollInterval > 0) {
[self addTimer];
}
}
}
#pragma mark - timer
- (void)addTimer {
if (_timer || _autoScrollInterval <= 0) {
return;
}
_timer = [NSTimer timerWithTimeInterval:_autoScrollInterval target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
- (void)removeTimer {
if (!_timer) {
return;
}
[_timer invalidate];
_timer = nil;
}
- (void)timerFired:(NSTimer *)timer {
if (!self.superview || !self.window || _numberOfItems == 0 || self.tracking) {
return;
}
[self scrollToNearlyIndexAtDirection:TYPagerScrollDirectionRight animate:YES];
}
#pragma mark - getter
- (TYCyclePagerViewLayout *)layout {
if (!_layout) {
if (_dataSourceFlags.layoutForPagerView) {
_layout = [_dataSource layoutForPagerView:self];
_layout.isInfiniteLoop = _isInfiniteLoop;
}
if (_layout.itemSize.width <= 0 || _layout.itemSize.height <= 0) {
_layout = nil;
}
}
return _layout;
}
- (NSInteger)curIndex {
return _indexSection.index;
}
- (CGPoint)contentOffset {
return _collectionView.contentOffset;
}
- (BOOL)tracking {
return _collectionView.tracking;
}
- (BOOL)dragging {
return _collectionView.dragging;
}
- (BOOL)decelerating {
return _collectionView.decelerating;
}
- (UIView *)backgroundView {
return _collectionView.backgroundView;
}
- (__kindof UICollectionViewCell *)curIndexCell {
return [_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:_indexSection.index inSection:_indexSection.section]];
}
- (NSArray<__kindof UICollectionViewCell *> *)visibleCells {
return _collectionView.visibleCells;
}
- (NSArray *)visibleIndexs {
NSMutableArray *indexs = [NSMutableArray array];
for (NSIndexPath *indexPath in _collectionView.indexPathsForVisibleItems) {
[indexs addObject:@(indexPath.item)];
}
return [indexs copy];
}
#pragma mark - setter
- (void)setBackgroundView:(UIView *)backgroundView {
[_collectionView setBackgroundView:backgroundView];
}
- (void)setAutoScrollInterval:(CGFloat)autoScrollInterval {
_autoScrollInterval = autoScrollInterval;
[self removeTimer];
if (autoScrollInterval > 0 && self.superview) {
[self addTimer];
}
}
- (void)setDelegate:(id<TYCyclePagerViewDelegate>)delegate {
_delegate = delegate;
_delegateFlags.pagerViewDidScroll = [delegate respondsToSelector:@selector(pagerViewDidScroll:)];
_delegateFlags.didScrollFromIndexToNewIndex = [delegate respondsToSelector:@selector(pagerView:didScrollFromIndex:toIndex:)];
_delegateFlags.initializeTransformAttributes = [delegate respondsToSelector:@selector(pagerView:initializeTransformAttributes:)];
_delegateFlags.applyTransformToAttributes = [delegate respondsToSelector:@selector(pagerView:applyTransformToAttributes:)];
if (self.collectionView && self.collectionView.collectionViewLayout) {
((TYCyclePagerTransformLayout *)self.collectionView.collectionViewLayout).delegate = _delegateFlags.applyTransformToAttributes ? self : nil;
}
}
- (void)setDataSource:(id<TYCyclePagerViewDataSource>)dataSource {
_dataSource = dataSource;
_dataSourceFlags.cellForItemAtIndex = [dataSource respondsToSelector:@selector(pagerView:cellForItemAtIndex:)];
_dataSourceFlags.layoutForPagerView = [dataSource respondsToSelector:@selector(layoutForPagerView:)];
}
#pragma mark - public
- (void)reloadData {
_didReloadData = YES;
_needResetIndex = YES;
[self setNeedClearLayout];
[self clearLayout];
[self updateData];
}
// not clear layout
- (void)updateData {
[self updateLayout];
_numberOfItems = [_dataSource numberOfItemsInPagerView:self];
[_collectionView reloadData];
if (!_didLayout && !CGRectIsEmpty(self.collectionView.frame) && _indexSection.index < 0) {
_didLayout = YES;
}
BOOL needResetIndex = _needResetIndex && _reloadDataNeedResetIndex;
_needResetIndex = NO;
if (needResetIndex) {
[self removeTimer];
}
[self resetPagerViewAtIndex:(_indexSection.index < 0 && !CGRectIsEmpty(self.collectionView.frame)) || needResetIndex ? 0 :_indexSection.index];
if (needResetIndex) {
[self addTimer];
}
}
- (void)scrollToNearlyIndexAtDirection:(TYPagerScrollDirection)direction animate:(BOOL)animate {
TYIndexSection indexSection = [self nearlyIndexPathAtDirection:direction];
[self scrollToItemAtIndexSection:indexSection animate:animate];
}
- (void)scrollToItemAtIndex:(NSInteger)index animate:(BOOL)animate {
if (!_didLayout && _didReloadData) {
_firstScrollIndex = index;
}else {
_firstScrollIndex = -1;
}
if (!_isInfiniteLoop) {
[self scrollToItemAtIndexSection:TYMakeIndexSection(index, 0) animate:animate];
return;
}
[self scrollToItemAtIndexSection:TYMakeIndexSection(index, index >= self.curIndex ? _indexSection.section : _indexSection.section+1) animate:animate];
}
- (void)scrollToItemAtIndexSection:(TYIndexSection)indexSection animate:(BOOL)animate {
if (_numberOfItems <= 0 || ![self isValidIndexSection:indexSection]) {
//NSLog(@"scrollToItemAtIndex: item indexSection is invalid!");
return;
}
if (animate && [_delegate respondsToSelector:@selector(pagerViewWillBeginScrollingAnimation:)]) {
[_delegate pagerViewWillBeginScrollingAnimation:self];
}
CGFloat offset = [self caculateOffsetXAtIndexSection:indexSection];
[_collectionView setContentOffset:CGPointMake(offset, _collectionView.contentOffset.y) animated:animate];
}
- (void)registerClass:(Class)Class forCellWithReuseIdentifier:(NSString *)identifier {
[_collectionView registerClass:Class forCellWithReuseIdentifier:identifier];
}
- (void)registerNib:(UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier {
[_collectionView registerNib:nib forCellWithReuseIdentifier:identifier];
}
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndex:(NSInteger)index {
UICollectionViewCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:[NSIndexPath indexPathForItem:index inSection:_dequeueSection]];
return cell;
}
#pragma mark - configure layout
- (void)updateLayout {
if (!self.layout) {
return;
}
self.layout.isInfiniteLoop = _isInfiniteLoop;
((TYCyclePagerTransformLayout *)_collectionView.collectionViewLayout).layout = self.layout;
}
- (void)clearLayout {
if (_needClearLayout) {
_layout = nil;
_needClearLayout = NO;
}
}
- (void)setNeedClearLayout {
_needClearLayout = YES;
}
- (void)setNeedUpdateLayout {
if (!self.layout) {
return;
}
[self clearLayout];
[self updateLayout];
[_collectionView.collectionViewLayout invalidateLayout];
[self resetPagerViewAtIndex:_indexSection.index < 0 ? 0 :_indexSection.index];
}
#pragma mark - pager index
- (BOOL)isValidIndexSection:(TYIndexSection)indexSection {
return indexSection.index >= 0 && indexSection.index < _numberOfItems && indexSection.section >= 0 && indexSection.section < kPagerViewMaxSectionCount;
}
- (TYIndexSection)nearlyIndexPathAtDirection:(TYPagerScrollDirection)direction{
return [self nearlyIndexPathForIndexSection:_indexSection direction:direction];
}
- (TYIndexSection)nearlyIndexPathForIndexSection:(TYIndexSection)indexSection direction:(TYPagerScrollDirection)direction {
if (indexSection.index < 0 || indexSection.index >= _numberOfItems) {
return indexSection;
}
if (!_isInfiniteLoop) {
if (direction == TYPagerScrollDirectionRight && indexSection.index == _numberOfItems - 1) {
return _autoScrollInterval > 0 ? TYMakeIndexSection(0, 0) : indexSection;
} else if (direction == TYPagerScrollDirectionRight) {
return TYMakeIndexSection(indexSection.index+1, 0);
}
if (indexSection.index == 0) {
return _autoScrollInterval > 0 ? TYMakeIndexSection(_numberOfItems - 1, 0) : indexSection;
}
return TYMakeIndexSection(indexSection.index-1, 0);
}
if (direction == TYPagerScrollDirectionRight) {
if (indexSection.index < _numberOfItems-1) {
return TYMakeIndexSection(indexSection.index+1, indexSection.section);
}
if (indexSection.section >= kPagerViewMaxSectionCount-1) {
return TYMakeIndexSection(indexSection.index, kPagerViewMaxSectionCount-1);
}
return TYMakeIndexSection(0, indexSection.section+1);
}
if (indexSection.index > 0) {
return TYMakeIndexSection(indexSection.index-1, indexSection.section);
}
if (indexSection.section <= 0) {
return TYMakeIndexSection(indexSection.index, 0);
}
return TYMakeIndexSection(_numberOfItems-1, indexSection.section-1);
}
- (TYIndexSection)caculateIndexSectionWithOffsetX:(CGFloat)offsetX {
if (_numberOfItems <= 0) {
return TYMakeIndexSection(0, 0);
}
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)_collectionView.collectionViewLayout;
CGFloat leftEdge = _isInfiniteLoop ? _layout.sectionInset.left : _layout.onlyOneSectionInset.left;
CGFloat width = CGRectGetWidth(_collectionView.frame);
CGFloat middleOffset = offsetX + width/2;
CGFloat itemWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
NSInteger curIndex = 0;
NSInteger curSection = 0;
if (middleOffset - leftEdge >= 0) {
NSInteger itemIndex = (middleOffset - leftEdge+layout.minimumInteritemSpacing/2)/itemWidth;
if (itemIndex < 0) {
itemIndex = 0;
}else if (itemIndex >= _numberOfItems*kPagerViewMaxSectionCount) {
itemIndex = _numberOfItems*kPagerViewMaxSectionCount-1;
}
curIndex = itemIndex%_numberOfItems;
curSection = itemIndex/_numberOfItems;
}
return TYMakeIndexSection(curIndex, curSection);
}
- (CGFloat)caculateOffsetXAtIndexSection:(TYIndexSection)indexSection{
if (_numberOfItems == 0) {
return 0;
}
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)_collectionView.collectionViewLayout;
UIEdgeInsets edge = _isInfiniteLoop ? _layout.sectionInset : _layout.onlyOneSectionInset;
CGFloat leftEdge = edge.left;
CGFloat rightEdge = edge.right;
CGFloat width = CGRectGetWidth(_collectionView.frame);
CGFloat itemWidth = layout.itemSize.width + layout.minimumInteritemSpacing;
CGFloat offsetX = 0;
if (!_isInfiniteLoop && !_layout.itemHorizontalCenter && indexSection.index == _numberOfItems - 1) {
offsetX = leftEdge + itemWidth*(indexSection.index + indexSection.section*_numberOfItems) - (width - itemWidth) - layout.minimumInteritemSpacing + rightEdge;
}else {
offsetX = leftEdge + itemWidth*(indexSection.index + indexSection.section*_numberOfItems) - layout.minimumInteritemSpacing/2 - (width - itemWidth)/2;
}
return MAX(offsetX, 0);
}
- (void)resetPagerViewAtIndex:(NSInteger)index {
if (_didLayout && _firstScrollIndex >= 0) {
index = _firstScrollIndex;
_firstScrollIndex = -1;
}
if (index < 0) {
return;
}
if (index >= _numberOfItems) {
index = 0;
}
[self scrollToItemAtIndexSection:TYMakeIndexSection(index, _isInfiniteLoop ? kPagerViewMaxSectionCount/3 : 0) animate:NO];
if (!_isInfiniteLoop && _indexSection.index < 0) {
[self scrollViewDidScroll:_collectionView];
}
}
- (void)recyclePagerViewIfNeed {
if (!_isInfiniteLoop) {
return;
}
if (_indexSection.section > kPagerViewMaxSectionCount - kPagerViewMinSectionCount || _indexSection.section < kPagerViewMinSectionCount) {
[self resetPagerViewAtIndex:_indexSection.index];
}
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return _isInfiniteLoop ? kPagerViewMaxSectionCount : 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
_numberOfItems = [_dataSource numberOfItemsInPagerView:self];
return _numberOfItems;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
_dequeueSection = indexPath.section;
if (_dataSourceFlags.cellForItemAtIndex) {
return [_dataSource pagerView:self cellForItemAtIndex:indexPath.row];
}
NSAssert(NO, @"pagerView cellForItemAtIndex: is nil!");
return nil;
}
#pragma mark - UICollectionViewDelegateFlowLayout
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
if (!_isInfiniteLoop) {
return _layout.onlyOneSectionInset;
}
if (section == 0 ) {
return _layout.firstSectionInset;
}else if (section == kPagerViewMaxSectionCount -1) {
return _layout.lastSectionInset;
}
return _layout.middleSectionInset;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
if ([_delegate respondsToSelector:@selector(pagerView:didSelectedItemCell:atIndex:)]) {
[_delegate pagerView:self didSelectedItemCell:cell atIndex:indexPath.item];
}
if ([_delegate respondsToSelector:@selector(pagerView:didSelectedItemCell:atIndexSection:)]) {
[_delegate pagerView:self didSelectedItemCell:cell atIndexSection:TYMakeIndexSection(indexPath.item, indexPath.section)];
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (!_didLayout) {
return;
}
TYIndexSection newIndexSection = [self caculateIndexSectionWithOffsetX:scrollView.contentOffset.x];
if (_numberOfItems <= 0 || ![self isValidIndexSection:newIndexSection]) {
NSLog(@"inVlaidIndexSection:(%ld,%ld)!",(long)newIndexSection.index,(long)newIndexSection.section);
return;
}
TYIndexSection indexSection = _indexSection;
_indexSection = newIndexSection;
if (_delegateFlags.pagerViewDidScroll) {
[_delegate pagerViewDidScroll:self];
}
if (_delegateFlags.didScrollFromIndexToNewIndex && !TYEqualIndexSection(_indexSection, indexSection)) {
//NSLog(@"curIndex %ld",(long)_indexSection.index);
[_delegate pagerView:self didScrollFromIndex:MAX(indexSection.index, 0) toIndex:_indexSection.index];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
if (_autoScrollInterval > 0) {
[self removeTimer];
}
_beginDragIndexSection = [self caculateIndexSectionWithOffsetX:scrollView.contentOffset.x];
if ([_delegate respondsToSelector:@selector(pagerViewWillBeginDragging:)]) {
[_delegate pagerViewWillBeginDragging:self];
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (fabs(velocity.x) < 0.35 || !TYEqualIndexSection(_beginDragIndexSection, _indexSection)) {
targetContentOffset->x = [self caculateOffsetXAtIndexSection:_indexSection];
return;
}
TYPagerScrollDirection direction = TYPagerScrollDirectionRight;
if ((scrollView.contentOffset.x < 0 && targetContentOffset->x <= 0) || (targetContentOffset->x < scrollView.contentOffset.x && scrollView.contentOffset.x < scrollView.contentSize.width - scrollView.frame.size.width)) {
direction = TYPagerScrollDirectionLeft;
}
TYIndexSection indexSection = [self nearlyIndexPathForIndexSection:_indexSection direction:direction];
targetContentOffset->x = [self caculateOffsetXAtIndexSection:indexSection];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (_autoScrollInterval > 0) {
[self addTimer];
}
if ([_delegate respondsToSelector:@selector(pagerViewDidEndDragging:willDecelerate:)]) {
[_delegate pagerViewDidEndDragging:self willDecelerate:decelerate];
}
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
if ([_delegate respondsToSelector:@selector(pagerViewWillBeginDecelerating:)]) {
[_delegate pagerViewWillBeginDecelerating:self];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self recyclePagerViewIfNeed];
if ([_delegate respondsToSelector:@selector(pagerViewDidEndDecelerating:)]) {
[_delegate pagerViewDidEndDecelerating:self];
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self recyclePagerViewIfNeed];
if ([_delegate respondsToSelector:@selector(pagerViewDidEndScrollingAnimation:)]) {
[_delegate pagerViewDidEndScrollingAnimation:self];
}
}
#pragma mark - TYCyclePagerTransformLayoutDelegate
- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout initializeTransformAttributes:(UICollectionViewLayoutAttributes *)attributes {
if (_delegateFlags.initializeTransformAttributes) {
[_delegate pagerView:self initializeTransformAttributes:attributes];
}
}
- (void)pagerViewTransformLayout:(TYCyclePagerTransformLayout *)pagerViewTransformLayout applyTransformToAttributes:(UICollectionViewLayoutAttributes *)attributes {
if (_delegateFlags.applyTransformToAttributes) {
[_delegate pagerView:self applyTransformToAttributes:attributes];
}
}
- (void)layoutSubviews {
[super layoutSubviews];
BOOL needUpdateLayout = !CGRectEqualToRect(_collectionView.frame, self.bounds);
_collectionView.frame = self.bounds;
if ((_indexSection.section < 0 || needUpdateLayout) && (_numberOfItems > 0 || _didReloadData)) {
_didLayout = YES;
[self setNeedUpdateLayout];
}
}
- (void)dealloc {
((TYCyclePagerTransformLayout *)_collectionView.collectionViewLayout).delegate = nil;
_collectionView.delegate = nil;
_collectionView.dataSource = nil;
}
@end

View File

@@ -0,0 +1,47 @@
//
// TYPageControl.h
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/20.
// Copyright © 2017年 tany. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface TYPageControl : UIControl
@property (nonatomic, assign) NSInteger numberOfPages; // default is 0
@property (nonatomic, assign) NSInteger currentPage; // default is 0. value pinned to 0..numberOfPages-1
@property (nonatomic, assign) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO
@property (nonatomic, assign) CGFloat pageIndicatorSpaing;
@property (nonatomic, assign) UIEdgeInsets contentInset; // center will ignore this
@property (nonatomic, assign ,readonly) CGSize contentSize; // real content size
// override super
//@property (nonatomic, assign) UIControlContentVerticalAlignment contentVerticalAlignment; // how to position content vertically inside control. default is center
//@property (nonatomic, assign) UIControlContentHorizontalAlignment contentHorizontalAlignment; // how to position content hozontally inside control. default is center
// indicatorTint color
@property (nullable, nonatomic,strong) UIColor *pageIndicatorTintColor;
@property (nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor;
// indicator image
@property (nullable, nonatomic,strong) UIImage *pageIndicatorImage;
@property (nullable, nonatomic,strong) UIImage *currentPageIndicatorImage;
@property (nonatomic, assign) UIViewContentMode indicatorImageContentMode; // default is UIViewContentModeCenter
@property (nonatomic, assign) CGSize pageIndicatorSize; // indicator size
@property (nonatomic, assign) CGSize currentPageIndicatorSize; // default pageIndicatorSize
@property (nonatomic, assign) CGFloat animateDuring; // default 0.3
- (void)setCurrentPage:(NSInteger)currentPage animate:(BOOL)animate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,285 @@
//
// TYPageControl.m
// TYCyclePagerViewDemo
//
// Created by tany on 2017/6/20.
// Copyright © 2017 tany. All rights reserved.
//
#import "TYPageControl.h"
@interface TYPageControl ()
// UI
@property (nonatomic, strong) NSArray<UIImageView *> *indicatorViews;
// Data
@property (nonatomic, assign) BOOL forceUpdate;
@end
@implementation TYPageControl
#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self configurePropertys];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self configurePropertys];
}
return self;
}
- (void)configurePropertys {
self.userInteractionEnabled = NO;
_forceUpdate = NO;
_animateDuring = 0.3;
_pageIndicatorSpaing = 10;
_indicatorImageContentMode = UIViewContentModeCenter;
_pageIndicatorSize = CGSizeMake(6,6);
_currentPageIndicatorSize = _pageIndicatorSize;
_pageIndicatorTintColor = [UIColor colorWithRed:128/255. green:128/255. blue:128/255. alpha:1];
_currentPageIndicatorTintColor = [UIColor whiteColor];
}
- (void)willMoveToSuperview:(UIView *)newSuperview {
[super willMoveToSuperview:newSuperview];
if (newSuperview) {
_forceUpdate = YES;
[self updateIndicatorViews];
_forceUpdate = NO;
}
}
#pragma mark - getter setter
- (CGSize)contentSize {
CGFloat width = (_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) + _pageIndicatorSize.width + _contentInset.left +_contentInset.right;
CGFloat height = _currentPageIndicatorSize.height + _contentInset.top + _contentInset.bottom;
return CGSizeMake(width, height);
}
- (void)setNumberOfPages:(NSInteger)numberOfPages {
if (numberOfPages == _numberOfPages) {
return;
}
_numberOfPages = numberOfPages;
if (_currentPage >= numberOfPages) {
_currentPage = 0;
}
[self updateIndicatorViews];
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
- (void)setCurrentPage:(NSInteger)currentPage {
if (_currentPage == currentPage || _indicatorViews.count <= currentPage) {
return;
}
_currentPage = currentPage;
if (!CGSizeEqualToSize(_currentPageIndicatorSize, _pageIndicatorSize)) {
[self setNeedsLayout];
}
[self updateIndicatorViewsBehavior];
if (self.userInteractionEnabled) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
- (void)setCurrentPage:(NSInteger)currentPage animate:(BOOL)animate {
if (animate) {
[UIView animateWithDuration:_animateDuring animations:^{
[self setCurrentPage:currentPage];
}];
}else {
[self setCurrentPage:currentPage];
}
}
- (void)setPageIndicatorImage:(UIImage *)pageIndicatorImage {
_pageIndicatorImage = pageIndicatorImage;
[self updateIndicatorViewsBehavior];
}
- (void)setCurrentPageIndicatorImage:(UIImage *)currentPageIndicatorImage {
_currentPageIndicatorImage = currentPageIndicatorImage;
[self updateIndicatorViewsBehavior];
}
- (void)setPageIndicatorTintColor:(UIColor *)pageIndicatorTintColor {
_pageIndicatorTintColor = pageIndicatorTintColor;
[self updateIndicatorViewsBehavior];
}
- (void)setCurrentPageIndicatorTintColor:(UIColor *)currentPageIndicatorTintColor {
_currentPageIndicatorTintColor = currentPageIndicatorTintColor;
[self updateIndicatorViewsBehavior];
}
- (void)setPageIndicatorSize:(CGSize)pageIndicatorSize {
if (CGSizeEqualToSize(_pageIndicatorSize, pageIndicatorSize)) {
return;
}
_pageIndicatorSize = pageIndicatorSize;
if (CGSizeEqualToSize(_currentPageIndicatorSize, CGSizeZero) || (_currentPageIndicatorSize.width < pageIndicatorSize.width && _currentPageIndicatorSize.height < pageIndicatorSize.height)) {
_currentPageIndicatorSize = pageIndicatorSize;
}
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
- (void)setPageIndicatorSpaing:(CGFloat)pageIndicatorSpaing {
_pageIndicatorSpaing = pageIndicatorSpaing;
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
- (void)setCurrentPageIndicatorSize:(CGSize)currentPageIndicatorSize {
if (CGSizeEqualToSize(_currentPageIndicatorSize, currentPageIndicatorSize)) {
return;
}
_currentPageIndicatorSize = currentPageIndicatorSize;
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
- (void)setContentHorizontalAlignment:(UIControlContentHorizontalAlignment)contentHorizontalAlignment {
[super setContentHorizontalAlignment:contentHorizontalAlignment];
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
- (void)setContentVerticalAlignment:(UIControlContentVerticalAlignment)contentVerticalAlignment {
[super setContentVerticalAlignment:contentVerticalAlignment];
if (_indicatorViews.count > 0) {
[self setNeedsLayout];
}
}
#pragma mark - update indicator
- (void)updateIndicatorViews {
if (!self.superview && !_forceUpdate) {
return;
}
if (_indicatorViews.count == _numberOfPages) {
[self updateIndicatorViewsBehavior];
return;
}
NSMutableArray *indicatorViews = _indicatorViews ? [_indicatorViews mutableCopy] :[NSMutableArray array];
if (indicatorViews.count < _numberOfPages) {
for (NSInteger idx = indicatorViews.count; idx < _numberOfPages; ++idx) {
UIImageView *indicatorView = [[UIImageView alloc]init];
indicatorView.contentMode = _indicatorImageContentMode;
[self addSubview:indicatorView];
[indicatorViews addObject:indicatorView];
}
}else if (indicatorViews.count > _numberOfPages) {
for (NSInteger idx = indicatorViews.count - 1; idx >= _numberOfPages; --idx) {
UIImageView *indicatorView = indicatorViews[idx];
[indicatorView removeFromSuperview];
[indicatorViews removeObjectAtIndex:idx];
}
}
_indicatorViews = [indicatorViews copy];
[self updateIndicatorViewsBehavior];
}
- (void)updateIndicatorViewsBehavior {
if (_indicatorViews.count == 0 || (!self.superview && !_forceUpdate)) {
return;
}
if (_hidesForSinglePage && _indicatorViews.count == 1) {
UIImageView *indicatorView = _indicatorViews.lastObject;
indicatorView.hidden = YES;
return;
}
NSInteger index = 0;
for (UIImageView *indicatorView in _indicatorViews) {
if (_pageIndicatorImage) {
indicatorView.contentMode = _indicatorImageContentMode;
indicatorView.image = _currentPage == index ? _currentPageIndicatorImage : _pageIndicatorImage;
}else {
indicatorView.image = nil;
indicatorView.backgroundColor = _currentPage == index ? _currentPageIndicatorTintColor : _pageIndicatorTintColor;
}
indicatorView.hidden = NO;
++index;
}
}
#pragma mark - layout
- (void)layoutIndicatorViews {
if (_indicatorViews.count == 0) {
return;
}
CGFloat orignX = 0;
CGFloat centerY = 0;
CGFloat pageIndicatorSpaing = _pageIndicatorSpaing;
switch (self.contentHorizontalAlignment) {
case UIControlContentHorizontalAlignmentCenter:
// ignore contentInset
orignX = (CGRectGetWidth(self.frame) - (_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) - _currentPageIndicatorSize.width)/2;
break;
case UIControlContentHorizontalAlignmentLeft:
orignX = _contentInset.left;
break;
case UIControlContentHorizontalAlignmentRight:
orignX = CGRectGetWidth(self.frame) - ((_indicatorViews.count - 1) * (_pageIndicatorSize.width + _pageIndicatorSpaing) + _currentPageIndicatorSize.width) - _contentInset.right;
break;
case UIControlContentHorizontalAlignmentFill:
orignX = _contentInset.left;
if (_indicatorViews.count > 1) {
pageIndicatorSpaing = (CGRectGetWidth(self.frame) - _contentInset.left - _contentInset.right - _pageIndicatorSize.width - (_indicatorViews.count - 1) * _pageIndicatorSize.width)/(_indicatorViews.count - 1);
}
break;
default:
break;
}
switch (self.contentVerticalAlignment) {
case UIControlContentVerticalAlignmentCenter:
centerY = CGRectGetHeight(self.frame)/2;
break;
case UIControlContentVerticalAlignmentTop:
centerY = _contentInset.top + _currentPageIndicatorSize.height/2;
break;
case UIControlContentVerticalAlignmentBottom:
centerY = CGRectGetHeight(self.frame) - _currentPageIndicatorSize.height/2 - _contentInset.bottom;
break;
case UIControlContentVerticalAlignmentFill:
centerY = (CGRectGetHeight(self.frame) - _contentInset.top - _contentInset.bottom)/2 + _contentInset.top;
break;
default:
break;
}
NSInteger index = 0;
for (UIImageView *indicatorView in _indicatorViews) {
if (_pageIndicatorImage) {
indicatorView.layer.cornerRadius = 0;
}else {
indicatorView.layer.cornerRadius = _currentPage == index ? _currentPageIndicatorSize.height/2 : _pageIndicatorSize.height/2;
}
CGSize size = index == _currentPage ? _currentPageIndicatorSize : _pageIndicatorSize;
indicatorView.frame = CGRectMake(orignX, centerY - size.height/2, size.width, size.height);
orignX += size.width + pageIndicatorSpaing;
++index;
}
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutIndicatorViews];
}
@end