二手车信息

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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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