背景

项目中需求完成一个不同宽度的图片的无限轮播图作用,并且每次翻滚,只滚到下一个图片。因为业界完成的轮播图作用都是等宽图片,所以需求从头根据“以假乱真”的原理,设计一款不同宽度的轮播作用;

演示作用

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

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

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

完成思路

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

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

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

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

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

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

完整的代码完成

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