纯代码关于UICollectionView水平分页
about UICollectionView Horizontal Paging with pure code
为了实现自动水平滚动,我自定义了一个collectionView,有100个section,每个section有5个item。然后添加一个NSTimer,原来的位置设置在collectionView的中间(第50段),终于成功了。 (ps:collectionView是tableView的headerView!)
但是我自己滚动的时候,不能正常滚动到下一页。总是出现两个项目(位置不规则),但我只想要一个。
(最新进展:
设置flowLayout.minimumLineSpacing = 0.0f.
后两个项目的边距变得规则(接近一个未知的固定值)。而且我发现如果我刚刚完成启动应用程序并自己滚动它,它表现正常。但是,如果等待 NSTimer 运行并滚动,它的行为将如我所说。总之,如果没有NSTimer,它表现正常。也许 NSTimer 有问题...)
例外:
原文其实:
实际最新
如果我需要覆盖 scrollViewDidScroll:
,怎么办?或者注意一些属性? 我的NSTimer有问题吗?
部分代码:
**POChannelPageHeader.h** (a UICollectionViewCell)
@class POChannelPageItem;
@interface POChannelPageHeader : UICollectionViewCell
@property (nonatomic, strong) POChannelPageItem *channelPageItem;
+ (UICollectionViewCell *)cellWithCollectionView:(UICollectionView *)collectionView;
**POChannelPageHeader.m**
@interface POChannelPageHeader ()
@property (weak, nonatomic) UILabel *titleLabel;
@property (weak, nonatomic) UIImageView *picView;
@end
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self makeView];
}
return self;
}
#pragma mark custom cell
- (void)makeView
{
UIImageView * picView = [[UIImageView alloc] init];
self.picView = picView;
self.picView.alpha = 0.1f;
self.picView.contentMode = UIViewContentModeScaleToFill;
[self.contentView addSubview:self.picView];
UILabel *titleLabel = [[UILabel alloc] init];
self.titleLabel = titleLabel;
self.titleLabel.font = [UIFont systemFontOfSize:15.0f];
self.titleLabel.textColor = [UIColor whiteColor];
[self.titleLabel setBackgroundColor:[UIColor colorWithWhite:0.0f alpha:0.6f]];
self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.contentView addSubview:self.titleLabel];
}
#pragma mark layoutSubviews
-(void)layoutSubviews
{
[super layoutSubviews];
self.picView.x = 0.0f;
self.picView.y = 0.0f;
self.picView.width = kScreenWidth ;
self.picView.height = 0.4 * kScreenHeight;
self.titleLabel.x = self.picView.x;
self.titleLabel.width = kScreenWidth;
CGFloat titleLabelPadding = 12.0f;
self.titleLabel.height = [NSString heightForText:self.titleLabel.text boundingRectWithWidth:TitleLabelWidth fontOfSize:15.0f] + titleLabelPadding;
self.titleLabel.y = self.picView.height - self.titleLabel.height;
}
#pragma mark - setter
- (void)setChannelPageItem:(POChannelPageItem *)channelPageItem
{
_channelPageItem = channelPageItem;
self.picView.alpha = 0.1f;
UIImage *placeholderImage = [UIImage imageWithColor:[UIColor colorWithWhite:0.0 alpha:0.2f]];
[self.picView sd_setImageWithURL:[NSURL URLWithString:_channelPageItem.imgsrc] placeholderImage:placeholderImage options:SDWebImageRetryFailed completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
[UIView animateWithDuration:0.5f animations:^{
self.picView.alpha = 1.0f;
}];
}];
self.titleLabel.text = [@" " stringByAppendingString:_channelPageItem.title];
}
#import "POChannelPageViewController.h" ( a UITableViewController)
#define POChannelHeaderID @"POChannelPageHeader"
#define POMaxSections 100
#define POChannelHeaderPages 5
@interface POChannelPageViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) UICollectionView *headerView;
@property (nonatomic, strong) NSMutableArray *headerNews;
@property (nonatomic, strong) NSTimer *timer;
@property (weak, nonatomic) UIPageControl *headerPage;
@end
- (void)viewDidLoad {
[super viewDidLoad];
[self makeHeaderView]
}
- (void)makeHeaderView
{
UICollectionViewFlowLayout *flowLayout= [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
flowLayout.itemSize = CGSizeMake(kScreenWidth, 0.4 * kScreenHeight);
UICollectionView *headerView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth , 0.4 *kScreenHeight) collectionViewLayout:flowLayout];
headerView.pagingEnabled = YES;
self.headerView = headerView;
self.headerView.delegate = self;
self.headerView.dataSource = self;
self.headerView.showsHorizontalScrollIndicator = NO;
[self.headerView registerClass:[POChannelPageHeader class] forCellWithReuseIdentifier:POChannelHeaderID];
self.tableView.tableHeaderView =self.headerView;
// add PageControl
UIPageControl *headerPage = [[UIPageControl alloc] init];
headerPage.x = kScreenWidth - 100.0f + 2.0f; // little adjustment
headerPage.height = 37;
headerPage.y = self.headerView.height - headerPage.height + 5.0f;// little adjustment
headerPage.width = 100.0f;
self.headerPage = headerPage;
self.headerPage.numberOfPages = POChannelHeaderPages;
self.headerPage.currentPage = 0;
self.headerPage.pageIndicatorTintColor = [UIColor lightGrayColor];
self.headerPage.currentPageIndicatorTintColor = [UIColor colorWithRed:0.735 green:1.000 blue:0.300 alpha:1.000];
[self.tableView addSubview:self.hederPage];
[self.headerView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:POMaxSections / 2] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
[self addTimer];
}
#pragma mark - add Timer
- (void)addTimer
{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.timer = timer;
}
#pragma mark - remove Timer
- (void)removeTimer
{
[self.timer invalidate];
self.timer = nil;
}
#pragma mark - resetIndexPath
- (NSIndexPath *)resetIndexPath
{
NSIndexPath *currentIndexPath = [[self.headerView indexPathsForVisibleItems] lastObject];
NSIndexPath *currentIndexPathReset = [NSIndexPath indexPathForItem:currentIndexPath.item inSection:POMaxSections / 2];
[self.headerView scrollToItemAtIndexPath:currentIndexPathReset atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
return currentIndexPathReset;
}
- (void)nextPage
{
NSIndexPath *currentIndexPathReset = [self resetIndexPath];
NSInteger nextItem = currentIndexPathReset.item + 1;
NSInteger nextSection = currentIndexPathReset.section;
if (nextItem == self.headerNews.count) {
nextItem = 0;
nextSection++;
}
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:nextItem inSection:nextSection];
[self.headerView scrollToItemAtIndexPath:nextIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.headerNews.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return POMaxSections;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
POChannelPageHeader *cell = [collectionView dequeueReusableCellWithReuseIdentifier:POChannelHeaderID forIndexPath:indexPath];
cell.channelPageItem = self.headerNews[indexPath.item];
return cell;
}
#pragma mark - UICollectionViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self removeTimer];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (scrollView == self.headerView) {
[self addTimer];
}
}
**// I'm not sure this method, because I study from a demo**
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView == self.tableView ){
**// especially here!!!**
NSInteger page = (NSInteger)(scrollView.contentOffset.x / scrollView.bounds.size.width + 0.5) % self.headerNews.count;
self.headerPage.currentPage = page;
} else if (scrollView ==self.tableView && scrollView.contentOffset.y ==0){
[self addTimer];
}
您能解释一下为什么要创建 100 个部分而不是一个包含 100 个项目的部分吗?无论如何,我看到 2 个错误:
1) 你有一张全宽图片和 "inter item space",但你在屏幕上没有这个 space 的位置(在你的情况下它不是中间项目 space,但是正确的部分插图或其他内容)
2) 你有一个右边的插图,但是你没有左边的
因此,如果您将初始位置设置为第一个单元格并滑动,您将在屏幕上看到一个插入部分。在下一次滑动时,您将看到前一个单元格、插图和下一个单元格的一小部分...这样,在 50 次滑动后,您将看到不可预测的屏幕偏移。
我画了一些方法来解决你的问题(对不起我的 fotoshop 技能 :D)。在图片中,黑色矩形是屏幕,红色是集合视图,绿色是单元格。 X 是单元格之间的 space。所以你需要增加集合视图的宽度,把它放在左边,用这个 space.
的一半值设置左右部分插图
导致您将拥有一张全屏宽度的图片,其中包含中间项 space,您只能在滑动时看到它。
编辑: 有示例项目 https://github.com/dimasv28/slider
为什么不为此使用 UIPageViewController?它似乎更适合您的问题。它旨在完全满足您的需求。
为了实现自动水平滚动,我自定义了一个collectionView,有100个section,每个section有5个item。然后添加一个NSTimer,原来的位置设置在collectionView的中间(第50段),终于成功了。 (ps:collectionView是tableView的headerView!)
但是我自己滚动的时候,不能正常滚动到下一页。总是出现两个项目(位置不规则),但我只想要一个。
(最新进展:
设置flowLayout.minimumLineSpacing = 0.0f.
后两个项目的边距变得规则(接近一个未知的固定值)。而且我发现如果我刚刚完成启动应用程序并自己滚动它,它表现正常。但是,如果等待 NSTimer 运行并滚动,它的行为将如我所说。总之,如果没有NSTimer,它表现正常。也许 NSTimer 有问题...)
例外:
原文其实:
实际最新
如果我需要覆盖 scrollViewDidScroll:
,怎么办?或者注意一些属性? 我的NSTimer有问题吗?
部分代码:
**POChannelPageHeader.h** (a UICollectionViewCell)
@class POChannelPageItem;
@interface POChannelPageHeader : UICollectionViewCell
@property (nonatomic, strong) POChannelPageItem *channelPageItem;
+ (UICollectionViewCell *)cellWithCollectionView:(UICollectionView *)collectionView;
**POChannelPageHeader.m**
@interface POChannelPageHeader ()
@property (weak, nonatomic) UILabel *titleLabel;
@property (weak, nonatomic) UIImageView *picView;
@end
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self makeView];
}
return self;
}
#pragma mark custom cell
- (void)makeView
{
UIImageView * picView = [[UIImageView alloc] init];
self.picView = picView;
self.picView.alpha = 0.1f;
self.picView.contentMode = UIViewContentModeScaleToFill;
[self.contentView addSubview:self.picView];
UILabel *titleLabel = [[UILabel alloc] init];
self.titleLabel = titleLabel;
self.titleLabel.font = [UIFont systemFontOfSize:15.0f];
self.titleLabel.textColor = [UIColor whiteColor];
[self.titleLabel setBackgroundColor:[UIColor colorWithWhite:0.0f alpha:0.6f]];
self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
[self.contentView addSubview:self.titleLabel];
}
#pragma mark layoutSubviews
-(void)layoutSubviews
{
[super layoutSubviews];
self.picView.x = 0.0f;
self.picView.y = 0.0f;
self.picView.width = kScreenWidth ;
self.picView.height = 0.4 * kScreenHeight;
self.titleLabel.x = self.picView.x;
self.titleLabel.width = kScreenWidth;
CGFloat titleLabelPadding = 12.0f;
self.titleLabel.height = [NSString heightForText:self.titleLabel.text boundingRectWithWidth:TitleLabelWidth fontOfSize:15.0f] + titleLabelPadding;
self.titleLabel.y = self.picView.height - self.titleLabel.height;
}
#pragma mark - setter
- (void)setChannelPageItem:(POChannelPageItem *)channelPageItem
{
_channelPageItem = channelPageItem;
self.picView.alpha = 0.1f;
UIImage *placeholderImage = [UIImage imageWithColor:[UIColor colorWithWhite:0.0 alpha:0.2f]];
[self.picView sd_setImageWithURL:[NSURL URLWithString:_channelPageItem.imgsrc] placeholderImage:placeholderImage options:SDWebImageRetryFailed completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
[UIView animateWithDuration:0.5f animations:^{
self.picView.alpha = 1.0f;
}];
}];
self.titleLabel.text = [@" " stringByAppendingString:_channelPageItem.title];
}
#import "POChannelPageViewController.h" ( a UITableViewController)
#define POChannelHeaderID @"POChannelPageHeader"
#define POMaxSections 100
#define POChannelHeaderPages 5
@interface POChannelPageViewController () <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
@property (weak, nonatomic) UICollectionView *headerView;
@property (nonatomic, strong) NSMutableArray *headerNews;
@property (nonatomic, strong) NSTimer *timer;
@property (weak, nonatomic) UIPageControl *headerPage;
@end
- (void)viewDidLoad {
[super viewDidLoad];
[self makeHeaderView]
}
- (void)makeHeaderView
{
UICollectionViewFlowLayout *flowLayout= [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
flowLayout.itemSize = CGSizeMake(kScreenWidth, 0.4 * kScreenHeight);
UICollectionView *headerView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth , 0.4 *kScreenHeight) collectionViewLayout:flowLayout];
headerView.pagingEnabled = YES;
self.headerView = headerView;
self.headerView.delegate = self;
self.headerView.dataSource = self;
self.headerView.showsHorizontalScrollIndicator = NO;
[self.headerView registerClass:[POChannelPageHeader class] forCellWithReuseIdentifier:POChannelHeaderID];
self.tableView.tableHeaderView =self.headerView;
// add PageControl
UIPageControl *headerPage = [[UIPageControl alloc] init];
headerPage.x = kScreenWidth - 100.0f + 2.0f; // little adjustment
headerPage.height = 37;
headerPage.y = self.headerView.height - headerPage.height + 5.0f;// little adjustment
headerPage.width = 100.0f;
self.headerPage = headerPage;
self.headerPage.numberOfPages = POChannelHeaderPages;
self.headerPage.currentPage = 0;
self.headerPage.pageIndicatorTintColor = [UIColor lightGrayColor];
self.headerPage.currentPageIndicatorTintColor = [UIColor colorWithRed:0.735 green:1.000 blue:0.300 alpha:1.000];
[self.tableView addSubview:self.hederPage];
[self.headerView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:POMaxSections / 2] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
[self addTimer];
}
#pragma mark - add Timer
- (void)addTimer
{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10.0f target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.timer = timer;
}
#pragma mark - remove Timer
- (void)removeTimer
{
[self.timer invalidate];
self.timer = nil;
}
#pragma mark - resetIndexPath
- (NSIndexPath *)resetIndexPath
{
NSIndexPath *currentIndexPath = [[self.headerView indexPathsForVisibleItems] lastObject];
NSIndexPath *currentIndexPathReset = [NSIndexPath indexPathForItem:currentIndexPath.item inSection:POMaxSections / 2];
[self.headerView scrollToItemAtIndexPath:currentIndexPathReset atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
return currentIndexPathReset;
}
- (void)nextPage
{
NSIndexPath *currentIndexPathReset = [self resetIndexPath];
NSInteger nextItem = currentIndexPathReset.item + 1;
NSInteger nextSection = currentIndexPathReset.section;
if (nextItem == self.headerNews.count) {
nextItem = 0;
nextSection++;
}
NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:nextItem inSection:nextSection];
[self.headerView scrollToItemAtIndexPath:nextIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.headerNews.count;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return POMaxSections;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
POChannelPageHeader *cell = [collectionView dequeueReusableCellWithReuseIdentifier:POChannelHeaderID forIndexPath:indexPath];
cell.channelPageItem = self.headerNews[indexPath.item];
return cell;
}
#pragma mark - UICollectionViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self removeTimer];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (scrollView == self.headerView) {
[self addTimer];
}
}
**// I'm not sure this method, because I study from a demo**
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(scrollView == self.tableView ){
**// especially here!!!**
NSInteger page = (NSInteger)(scrollView.contentOffset.x / scrollView.bounds.size.width + 0.5) % self.headerNews.count;
self.headerPage.currentPage = page;
} else if (scrollView ==self.tableView && scrollView.contentOffset.y ==0){
[self addTimer];
}
您能解释一下为什么要创建 100 个部分而不是一个包含 100 个项目的部分吗?无论如何,我看到 2 个错误: 1) 你有一张全宽图片和 "inter item space",但你在屏幕上没有这个 space 的位置(在你的情况下它不是中间项目 space,但是正确的部分插图或其他内容) 2) 你有一个右边的插图,但是你没有左边的
因此,如果您将初始位置设置为第一个单元格并滑动,您将在屏幕上看到一个插入部分。在下一次滑动时,您将看到前一个单元格、插图和下一个单元格的一小部分...这样,在 50 次滑动后,您将看到不可预测的屏幕偏移。
我画了一些方法来解决你的问题(对不起我的 fotoshop 技能 :D)。在图片中,黑色矩形是屏幕,红色是集合视图,绿色是单元格。 X 是单元格之间的 space。所以你需要增加集合视图的宽度,把它放在左边,用这个 space.
的一半值设置左右部分插图导致您将拥有一张全屏宽度的图片,其中包含中间项 space,您只能在滑动时看到它。
编辑: 有示例项目 https://github.com/dimasv28/slider
为什么不为此使用 UIPageViewController?它似乎更适合您的问题。它旨在完全满足您的需求。