無縫輪播圖(Banner/Carousel)是 App 首頁常見組件。核心思路:在 UIScrollView 中佈局 N+2 張圖片(首尾各複製一張),利用 contentOffset 重置實現視覺上的無限循環。本文提供完整實現代碼並詳解關鍵邏輯。
number 張圖片(如 4 張)contentSize = (number + 2) * width:共 6 個頁面位置[最後一張][第1張][第2張][第3張][第4張][第1張複製]contentOffset = (1 * width, 0):直接顯示真正的第 1 張// MARK: - 初始化佈局
- (void)createView {
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 64, 320, 230)];
self.scrollView.backgroundColor = [UIColor yellowColor];
self.scrollView.pagingEnabled = YES;
self.scrollView.bounces = NO;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.delegate = self;
self.scrollView.contentSize = CGSizeMake(320 * (number + 2), 240);
// 佈局 N+2 個 UIImageView
for (int i = 0; i < number + 2; i++) {
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(320 * i + 3, 3, 314, 224)];
imgView.tag = 100 + i;
imgView.userInteractionEnabled = YES;
imgView.backgroundColor = [UIColor redColor];
[self.scrollView addSubview:imgView];
}
// 初始偏移:顯示真正的第一張
self.scrollView.contentOffset = CGPointMake(320, 0);
[self.view addSubview:self.scrollView];
[self addImage]; // 填充圖片
[self setupPageControl]; // 頁碼指示器
[self addTimer]; // 自動輪播定時器
}
// MARK: - 圖片填充邏輯(關鍵:首尾複製)
- (void)addImage {
// 1. 填充中間真實圖片 (索引 1~number)
for (int i = 0; i < number; i++) {
UIImageView *iv = (UIImageView *)[self.scrollView viewWithTag:101 + i];
iv.image = [UIImage imageNamed:self.imageNameArray[i]];
}
// 2. 首位複製最後一張 (索引 0)
UIImageView *first = (UIImageView *)[self.scrollView viewWithTag:100];
first.image = [UIImage imageNamed:self.imageNameArray[number - 1]];
// 3. 尾位複製第一張 (索引 number+1)
UIImageView *last = (UIImageView *)[self.scrollView viewWithTag:100 + number + 1];
last.image = [UIImage imageNamed:self.imageNameArray[0]];
}
// MARK: - 頁碼控制器點擊響應
- (void)handlePageControlAction:(UIPageControl *)sender {
CGPoint offset = CGPointMake(sender.currentPage * 320, 0);
[self.scrollView setContentOffset:offset animated:YES];
}
// MARK: - 自動輪播定時器
- (void)addTimer {
[NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
}
- (void)runTimer {
CGFloat x = self.scrollView.contentOffset.x;
[self.scrollView setContentOffset:CGPointMake(x + 320, 0) animated:YES];
}
// MARK: - UIScrollViewDelegate:無縫循環核心邏輯
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetX = scrollView.contentOffset.x;
CGFloat pageWidth = 320;
// 1. 滾動到「複製的最後一張」(索引 0) → 瞬間跳到真正的最後一張 (索引 number)
if (offsetX == 0) {
[scrollView setContentOffset:CGPointMake(pageWidth * number, 0) animated:NO];
}
// 2. 滾動到「複製的第一張」(索引 number+1) → 瞬間跳到真正的第一張 (索引 1)
else if (offsetX == pageWidth * (number + 1)) {
[scrollView setContentOffset:CGPointMake(pageWidth, 0) animated:NO];
}
// 3. 更新頁碼指示器 (扣除首偏移)
self.pageControl.currentPage = (offsetX / pageWidth) - 1;
}
若只有 N 張,從最後一張滑到第一張時會出現「倒退」動畫;複製首尾各一張,配合 animated:NO 瞬間重置 contentOffset,用戶感知為平滑循環。
scrollViewWillBeginDragging 中 invalidate,scrollViewDidEndDragging 中重啟viewDidDisappear)銷毀定時器防止內存洩漏UICollectionView 復用機制替代 UIScrollView 手動佈局若使用 Masonry/SnapKit,約束寫法:
[imgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.equalTo(self.scrollView);
make.width.equalTo(self.scrollView);
make.left.equalTo(self.scrollView.mas_left).offset(320 * i);
}];
掌握「首尾複製 + contentOffset 瞬間重置 + delegate 監聽」三大核心,即可實現流暢無縫輪播。實際項目中建議封裝為通用 CycleScrollView 組件,支持圖片點擊回調、自定義頁碼樣式、網絡圖片加載等擴展功能。