注册
iOS

iOS实现宽度不同无限轮播图

背景


项目中需要实现一个不同宽度的图片的无限轮播图效果,而且每次滚动,只滚到下一个图片。由于业界实现的轮播图效果都是等宽图片,所以需要重新根据“以假乱真”的原理,设计一款不同宽度的轮播效果;


演示效果


底部是个collectionView,顶部盖了个透明的scrollView,传入的数据源是:

NSArray *imageWidthArray = @[@(200), @(60), @(120)];

66020ec1a22f3210839436104083d818.gif


实现思路


  1. 传入一个存储图片宽度的数组,计算出屏幕可见的个数,比如下图,假如可见数为3个;

  2. 左、右两侧各有2个灰块,用于实现以假乱真的数据;(两侧各需生成的灰块数=屏幕可见数-1)

  • 比如当前看到123,左滑会滚到231,再左滑会滚到312,此时设置contentOffset,切到前面那个312;
  • 比如当前看到123,右滑会滚到312,再右滑会滚到231,此时设置contentOffset,切到后面那个231;
  1. 为了性能方面的考虑,使用的是collectionView;
  2. 关于每次滚动,只滚到下一个,实现方式则是在collectionView上面盖一个scrollView,设置其isPagingEnabled = YES; scrollView里面的页数和数据源保持一致(方便计算滚到哪个page);


7f18db2b6ffbbfc436b0c2a320df6eac.png

ad7154c979b1d5a678d6f2cf90a35583.png


完整的代码实现


Github Demo


ViewController:

#import "ViewController.h"
#import "MyCollectionViewCell.h"

#define padding 10.f
#define margin 16.f
#define scrollViewWidth (self.view.bounds.size.width - 2 * margin)
#define scrollViewHeight 200.f

@interface ViewController ()<UIScrollViewDelegate, UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) UIScrollView *topScrollView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) NSArray *imageWidthArray; // 用户传入,图片宽度数组
@property (nonatomic, assign) NSInteger canSeeViewCount; // 屏幕最多可见几个view
@property (nonatomic, strong) NSMutableArray *imageWidthMuArray;
@property (nonatomic, strong) NSMutableArray *imageContentOffsetXArray;
@property (nonatomic, strong) NSMutableArray *currentPageMuArray;

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupViewWithImageWidthArray:@[@(200), @(60), @(120)]];
//    [self setupViewWithImageWidthArray:@[@(150), @(80),@(60), @(120)]];
}

-(void)setupViewWithImageWidthArray:(NSArray *)imageWidthArray {
    // 根据机型宽度,计算屏幕可见数量
    self.canSeeViewCount = imageWidthArray.count;
    CGFloat checkWidth = 0;
    for (NSInteger i = 0; i < imageWidthArray.count; i ++) {
        checkWidth += [imageWidthArray[i] floatValue];
        if (checkWidth >= scrollViewWidth) {
            self.canSeeViewCount = i + 1;
        }
    }

    self.imageWidthArray = imageWidthArray;
    self.imageContentOffsetXArray = [NSMutableArray arrayWithCapacity:self.imageWidthArray.count];

    // 插入头尾数据(前后插入可见数-1个)、生成currentPageMuArray
    self.imageWidthMuArray = [NSMutableArray array];
    self.currentPageMuArray = [NSMutableArray array];
    for (NSInteger i = self.imageWidthArray.count - (self.canSeeViewCount - 1); i < self.imageWidthArray.count; i ++) {
        [self.imageWidthMuArray addObject:self.imageWidthArray[i]];
        [self.currentPageMuArray addObject:@(i)];
    }
    [self.imageWidthMuArray addObjectsFromArray:self.imageWidthArray];

    for (NSInteger i = 0; i < self.imageWidthArray.count; i ++) {
        [self.currentPageMuArray addObject:@(i)];
    }

    for (NSInteger i = 0; i < (self.canSeeViewCount - 1); i ++) {
        [self.imageWidthMuArray addObject:self.imageWidthArray[i]];
        [self.currentPageMuArray addObject:@(i)];
    }

    CGFloat collectionViewContentSizeWidth = 0;
    for (NSInteger i = 0; i < self.imageWidthMuArray.count; i ++) {
        CGFloat imageWidth = [self.imageWidthMuArray[i] floatValue];
        if ( i > 0) {
            collectionViewContentSizeWidth += padding;
        }
        [self.imageContentOffsetXArray addObject:@(collectionViewContentSizeWidth)];
        collectionViewContentSizeWidth += imageWidth;
    }

    // collectionView
    UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    flowLayout.minimumInteritemSpacing = padding;
    flowLayout.minimumLineSpacing = padding;

    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(margin, 100, scrollViewWidth, scrollViewHeight) collectionViewLayout:flowLayout];
    [collectionView registerClass:[MyCollectionViewCell class] forCellWithReuseIdentifier:@"MyCollectionViewCell"];
    collectionView.dataSource = self;
    collectionView.delegate = self;
    collectionView.bounces = NO;
    collectionView.showsHorizontalScrollIndicator = NO;
    collectionView.backgroundColor = [UIColor brownColor];
    [self.view addSubview:collectionView];
    collectionView.contentSize = CGSizeMake(collectionViewContentSizeWidth, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [collectionView setContentOffset:CGPointMake([self.imageContentOffsetXArray[self.canSeeViewCount - 1] floatValue], 0)];
    });
    self.collectionView = collectionView;

    // topScrollView
    UIScrollView *topScrollView = [[UIScrollView alloc] initWithFrame:collectionView.frame];
    topScrollView.showsHorizontalScrollIndicator = NO;
    [topScrollView setPagingEnabled:YES];
    topScrollView.backgroundColor = [UIColor clearColor];
    topScrollView.delegate = self;
    topScrollView.bounces = NO;
    [self.view addSubview:topScrollView];
    self.topScrollView = topScrollView;
    topScrollView.contentSize = CGSizeMake(self.imageWidthMuArray.count * scrollViewWidth, 0);
    [topScrollView setContentOffset:CGPointMake((self.canSeeViewCount - 1) * scrollViewWidth, 0)];

    // pageControl
    CGFloat pageControlHeight = 50.f;
    UIPageControl *pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(margin, 100 + scrollViewHeight-pageControlHeight, scrollViewWidth, pageControlHeight)];
    pageControl.numberOfPages = self.imageWidthArray.count;
    pageControl.currentPage = 0;
    [self.view addSubview:pageControl];
    self.pageControl = pageControl;
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView == self.collectionView) {
        return;
    }

    // 页面整数部分
    NSInteger floorPageIndex = floor(scrollView.contentOffset.x / scrollView.frame.size.width);

    // 小数部分
    CGFloat pageRate = scrollView.contentOffset.x / scrollView.frame.size.width - floor(scrollView.contentOffset.x / scrollView.frame.size.width);
    CGFloat imageContentOffsetX = [self.imageContentOffsetXArray[floorPageIndex] floatValue];
    CGFloat imageWidth = [self.imageWidthMuArray[floorPageIndex] floatValue];
    self.collectionView.contentOffset = CGPointMake(imageContentOffsetX + (imageWidth + 10.f) * pageRate, 0);
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSInteger rightIndex = (self.canSeeViewCount - 1) + (self.imageWidthArray.count) - 1;
    NSInteger leftIndex = (self.canSeeViewCount - 1) - 1;

    // 右边卡到尾时
    if (self.collectionView.contentOffset.x == [self.imageContentOffsetXArray[rightIndex] floatValue]) {
        [self.collectionView setContentOffset:CGPointMake([self.imageContentOffsetXArray[leftIndex] floatValue], 0)];
    }

    // 左边卡到头时
    else if (self.collectionView.contentOffset.x == 0) {
        [self.collectionView setContentOffset:CGPointMake([self.imageContentOffsetXArray[self.imageWidthArray.count] floatValue], 0)];
    }

    // 右边卡到尾时
    if (self.topScrollView.contentOffset.x == scrollViewWidth * rightIndex) {
        [self.topScrollView setContentOffset:CGPointMake(scrollViewWidth * leftIndex, 0)];
    }

    // 左边卡到头时
    if (self.topScrollView.contentOffset.x == 0) {
        [self.topScrollView setContentOffset:CGPointMake(scrollViewWidth * self.imageWidthArray.count, 0)];
    }

    // 设置currentPage
    NSInteger floorPageIndex = floor(scrollView.contentOffset.x / scrollView.frame.size.width);
    self.pageControl.currentPage = [self.currentPageMuArray[floorPageIndex] intValue];
}

#pragma mark - UICollectionViewDelegate, UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.imageWidthMuArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCollectionViewCell" forIndexPath:indexPath];
    cell.labelText = [NSString stringWithFormat:@"%.0f", [self.imageWidthMuArray[indexPath.item] floatValue]];
    return cell;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return CGSizeMake([self.imageWidthMuArray[indexPath.item] floatValue], scrollViewHeight);
}

@end

MyCollectionViewCell:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyCollectionViewCell : UICollectionViewCell

@property (nonatomic, copy) NSString *labelText;

@end

NS_ASSUME_NONNULL_END
#import "MyCollectionViewCell.h"
#import "Masonry.h"

@interface MyCollectionViewCell()

@property (nonatomic, strong) UILabel *label;

@end

@implementation MyCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self setupUI];
    }
    return self;
}

- (void)setupUI {
self.backgroundColor = [UIColor grayColor];
    UILabel *label = [[UILabel alloc] init];
    label.textAlignment = NSTextAlignmentCenter;
    label.font = [UIFont boldSystemFontOfSize:18];
    [self.contentView addSubview:label];
    [label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.contentView);
    }];
    self.label = label;
}

- (void)setLabelText:(NSString *)labelText {
    _labelText = labelText;
    self.label.text = labelText;
}

-(void)prepareForReuse {
    [super prepareForReuse];
    self.label.text = @"";
}
@end

作者:Wiley_Wan
链接:https://juejin.cn/post/7231443152212312123
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册