由于在项目开发过程中,经常会遇到app仿'支付宝的财富直通车'效果,本项目是直通车的升级版本,支持自定义滑动speed
、居中对齐
、最大允许惯性滑动page数
、自定义分页宽度
1.直通车控件特性
- cell宽度 < collectionView的Width
- 滑块在最左边或者最右边时,不用居中
- 滑动到其他位置,选择居中对齐
2.实现思路
自定义UICollectionViewFlowLayout,通过重写targetContentOffsetForProposedContentOffset:withScrollingVelocity:
方法,该方法的返回值为最终scrollview的contentOffset,如果不通过collectionView实现,同样可以补货scrollView的代理方法scrollViewWillEndDragging:withVelocity:targetContentOffset:
,下面我们看看自定义layout:
GLFreePagingLayout.h
@protocol GLFreePagingLayoutDelegate <NSObject>
- (void)targetCenterIndexPathForProposedIndexPath:(NSIndexPath *)indexpath;
@end
@interface GLFreePagingLayout : UICollectionViewFlowLayout
@property (nonatomic, weak) id<GLFreePagingLayoutDelegate> delegate;
/** speed 0~1.f,惯性大小,默认为1 */
@property (nonatomic, assign) CGFloat speed;
/** 居中对其 */
@property (nonatomic, assign) BOOL centerOn;
/** 开启居中对齐,在惯性作用下,松手后最大允许滑动的page数,可同时和speed作用,默认maxInertialCount为MAX */
@property (nonatomic, assign) NSInteger maxInertialCount;
/**
分页的宽度pageWidth,
1.在居中对齐情况下,如果依赖惯性滑动的距离>pageWidth * 0.5,那么就选择下一个cell对齐
2.默认pageWidth为collectionView的Width
*/
@property (nonatomic, assign) CGFloat pageWidth;
@end
GLFreePagingLayout.m
@implementation GLFreePagingLayout
- (instancetype)init {
self = [super init];
if (self) {
self.speed = 1.f;
self.maxInertialCount = NSIntegerMax;
}
return self;
}
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
// 保证滚动结束后视图的显示效果
if (self.collectionView.contentOffset.x < 0) {
[self scrollCenterToIndex:[NSIndexPath indexPathForRow:0 inSection:0]];
return proposedContentOffset;
}
if (self.collectionView.contentOffset.x > [super collectionViewContentSize].width - self.collectionView.frame.size.width) {
if (self.delegate && [self.delegate respondsToSelector:@selector(targetCenterIndexPathForProposedIndexPath:)]) {
NSInteger count = [self.collectionView numberOfItemsInSection:0];
[self scrollCenterToIndex:[NSIndexPath indexPathForRow:count - 1 inSection:0]];
}
return proposedContentOffset;
}
//在speed作用下的proposedContentOffset
CGFloat tmp = proposedContentOffset.x - self.collectionView.contentOffset.x;
tmp = tmp * self.speed;
proposedContentOffset.x = self.collectionView.contentOffset.x + tmp;
//如果没有开启居中对齐
if (!self.centerOn) {
return proposedContentOffset;
}
// 计算出最终显示的矩形框
CGRect rect;
rect.origin.y = 0;
rect.origin.x = proposedContentOffset.x;
rect.size = self.collectionView.frame.size;
if (self.pageWidth == 0) {
self.pageWidth = self.collectionView.frame.size.width;
}
CGFloat autoOffset = (self.maxInertialCount - 0.5) * self.pageWidth;
if (ABS(tmp) > autoOffset) {
rect = CGRectMake(self.collectionView.contentOffset.x + autoOffset * (tmp > 0 ? 1 : -1), proposedContentOffset.y, self.collectionView.frame.size.width, self.collectionView.frame.size.height);
}
// 获得 super 已经计算好的布局的属性
NSArray *arr = [super layoutAttributesForElementsInRect:rect];
// 计算 collectionView 最中心点的 x 值
CGFloat centerX = rect.origin.x + self.collectionView.frame.size.width * 0.5;
CGFloat minDelta = MAXFLOAT;
NSIndexPath *indexPath = nil;
for (UICollectionViewLayoutAttributes *attrs in arr) {
//cell视图
if (attrs.representedElementCategory == UICollectionElementCategoryCell && ABS(minDelta) > ABS(attrs.center.x - centerX)) {
minDelta = attrs.center.x - centerX;
indexPath = attrs.indexPath;
}
}
[self scrollCenterToIndex:indexPath];
return CGPointMake(rect.origin.x + minDelta, rect.origin.y);
}
//添加滚动动画
- (void)scrollCenterToIndex:(NSIndexPath *)indexPath {
if (self.delegate && [self.delegate respondsToSelector:@selector(targetCenterIndexPathForProposedIndexPath:)]) {
[self.delegate performSelector:@selector(targetCenterIndexPathForProposedIndexPath:) withObject:indexPath];
}
}
@end
3.Demo展示
GLFreePagingLayout *layout = [[GLFreePagingLayout alloc] init];
//开启居中对齐
layout.centerOn = YES;
//松手后惯性系数
layout.speed = 0.3;
//松手后最大滚动cell个数
layout.maxInertialCount = 2;
//分页宽度
layout.pageWidth = 300;
layout.delegate = self;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 400, self.view.frame.size.width, 200) collectionViewLayout:layout];
[_collectionView registerClass:UICollectionViewCell.class forCellWithReuseIdentifier:NSStringFromClass(UICollectionViewCell.class)];
_collectionView.backgroundColor = [UIColor redColor];
_collectionView.delegate = self;
_collectionView.dataSource = self;
![](https://img.haomeiwen.com/i17263634/876d9590bd37eb6c.png)
![](https://img.haomeiwen.com/i17263634/750b663c05bd4344.png)
网友评论