iOS廣告Banner,使用三個ImgView無限循環(huán)輪播
圖片加載使用 SDWebImage 第三方庫。
WTCycleRollView.h
#import
@interface WTCycleRollView : UIView
@property (nonatomic, assign) NSTimeInterval rollTimeInterval;
/**
返回 WTCycleRollView 對象
@param frame 位置尺寸
@return WTCycleRollView 對象
*/
+ (instancetype)cycleRollViewWithFrame:(CGRect)frame;
/**
設(shè)置圖片源、是否自動滾動、點擊圖片的回調(diào)
@param imgUrls 圖片源
@param autoCycleRoll 是否自動滾動
@param clickImgBlock 點擊圖片的回調(diào)
*/
- (void)setImgUrls:(NSArray *)imgUrls autoCycleRoll:(BOOL)autoCycleRoll clickImgBlock:(void(^)(NSInteger selectedIndex))clickImgBlock;
/** 暫停自動滾動 */
- (void)pauseCycleRoll;
/** 開始自動滾動【在暫停的狀態(tài)下】 */
- (void)startCycleRoll;
@end
WTCycleRollView.m
#import "WTCycleRollView.h"
#import "UIImageView+WebCache.h"
typedef void(^ClickImgBlock) (NSInteger);
@interface WTCycleRollView ()
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) UIImageView *leftImgView;
@property (nonatomic, strong) UIImageView *middleImgView;
@property (nonatomic, strong) UIImageView *rightImgView;
/** 圖片源 */
@property (nonatomic, strong) NSArray *imgUrls;
/** 當前顯示的圖片的 Index */
@property (nonatomic, assign) NSInteger showIndex;
/** 點擊圖片的回調(diào) Block */
@property (nonatomic, strong) ClickImgBlock clickImgBlock;
/** 自動滾動的計時器 */
@property (nonatomic, strong) NSTimer *cycleRollTimer;
/** 是否手動滑動 */
@property (nonatomic, assign) BOOL manual;
@end
@implementation WTCycleRollView
/** 默認循環(huán)滾動的時間間隔(s) */
static NSTimeInterval const DefaultRollTimeInterval = 4;
#pragma mark - 懶加載
- (UIImageView *)leftImgView {
if (!_leftImgView) {
_leftImgView = [[UIImageView alloc] initWithFrame:self.bounds];
}
return _leftImgView;
}
- (UIImageView *)middleImgView {
if (!_middleImgView) {
_middleImgView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.bounds), 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
/** 為中間正在顯示的 ImgView 添加點擊事件 */
UITapGestureRecognizer *tapGr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickImgView)];
[_middleImgView setUserInteractionEnabled:YES];
[_middleImgView addGestureRecognizer:tapGr];
}
return _middleImgView;
}
- (UIImageView *)rightImgView {
if (!_rightImgView) {
_rightImgView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetWidth(self.bounds) * 2, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds))];
}
return _rightImgView;
}
- (UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.delegate = self;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.pagingEnabled = YES;
}
return _scrollView;
}
- (UIPageControl *)pageControl {
if (!_pageControl) {
_pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.bounds) - 20, CGRectGetWidth(self.bounds), 20)];
_pageControl.layer.shadowOpacity = 0.3;
_pageControl.layer.shadowOffset = CGSizeMake(0, 0);
}
return _pageControl;
}
#pragma mark - 初始化
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self addSubview:self.scrollView];
[_scrollView addSubview:self.leftImgView];
[_scrollView addSubview:self.middleImgView];
[_scrollView addSubview:self.rightImgView];
[self addSubview:self.pageControl];
}
return self;
}
#pragma mark - 類方法 返回 WTCycleRollView
+ (instancetype)cycleRollViewWithFrame:(CGRect)frame {
return [[self alloc] initWithFrame:frame];
}
#pragma mark - 設(shè)置圖片顯示,自動滾動
- (void)setImgUrls:(NSArray *)imgUrls autoCycleRoll:(BOOL)autoCycleRoll clickImgBlock:(void (^)(NSInteger))clickImgBlock {
_clickImgBlock = clickImgBlock;
_imgUrls = imgUrls;
_pageControl.numberOfPages = [imgUrls count];
_pageControl.currentPage = 0;
if ([_imgUrls count] > 1) {
[_leftImgView sd_setImageWithURL:[NSURL URLWithString:[imgUrls lastObject]]];
[_middleImgView sd_setImageWithURL:[NSURL URLWithString:[imgUrls firstObject]]];
[_rightImgView sd_setImageWithURL:[NSURL URLWithString:[imgUrls objectAtIndex:1]]];
_scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.bounds) * 3, 0);
[_scrollView setContentOffset:CGPointMake(CGRectGetWidth(self.bounds), 0)];
if (autoCycleRoll) {
[self stopTimer];
_cycleRollTimer = [NSTimer scheduledTimerWithTimeInterval:DefaultRollTimeInterval target:self selector:@selector(cycleRoll) userInfo:nil repeats:YES];
} else {
[self stopTimer];
}
} else {
_scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.bounds), 0);
[_leftImgView removeFromSuperview];
[_rightImgView removeFromSuperview];
if ([imgUrls count]) {
[_middleImgView sd_setImageWithURL:[NSURL URLWithString:[imgUrls firstObject]]];
_middleImgView.frame = self.bounds;
}
}
}
#pragma mark - 自動滾動的事件
- (void)cycleRoll {
// 當手動滾動的時候,跳過此次循環(huán)
if (_manual) {
_manual = NO;
} else {
[_scrollView setContentOffset:CGPointMake(CGRectGetWidth(self.bounds) * 2, 0) animated:YES];
}
}
- (void)clickImgView {
!_clickImgBlock ? : _clickImgBlock(_showIndex);
}
#pragma mark - UIScrollView Delegate
/** ScrollView 滑動減速停止回調(diào)【這里根據(jù) ScrollView 的偏移量來判斷是向左滑還是向右滑】 */
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if ([_imgUrls count]) {
CGFloat offsetX = scrollView.contentOffset.x;
if (offsetX <= 0) {
// 向右滑,即將顯示上一張圖片(若當前顯示的是第一張,則上一張為最后一張)
self.showIndex = (_showIndex + [_imgUrls count] - 1) % [_imgUrls count];
} else if (offsetX >= CGRectGetWidth(self.bounds) * 2) {
// 向右滑,即將顯示下一張圖片(若當前顯示的是最后一張,則下一張為第一張)
self.showIndex = (_showIndex + 1) % [_imgUrls count];
}
}
}
/** 自動滑動【即代碼設(shè)置的滾動(setContentOffset:animated:)】,不會直接調(diào)用上述方法(滑動減速停止回調(diào)),但會調(diào)用此方法,在此方法再調(diào)用上述方法便可 */
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self scrollViewDidEndDecelerating:_scrollView];
}
/** 手動滑動手指觸摸屏幕的回調(diào),暫停計時器 */
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self pauseCycleRoll];
}
/** 手動滑動手指離開屏幕的回調(diào),開啟計時器 */
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
_manual = YES; // 這里開啟計時器會立即調(diào)用循環(huán)滾動的方法,用戶體驗不好,所以設(shè)置此參數(shù),讓其跳過這次循環(huán)滾動事件
[self startCycleRoll];
}
/** 根據(jù) ScrollView 偏移量設(shè)置 PageControl 的當前頁 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([_imgUrls count]) {
CGFloat offsetX = scrollView.contentOffset.x;
if (offsetX <= CGRectGetWidth(self.bounds) * 0.5) {
_pageControl.currentPage = (_showIndex + [_imgUrls count] - 1) % [_imgUrls count];
} else if (offsetX >= CGRectGetWidth(self.bounds) * 1.5) {
_pageControl.currentPage = (_showIndex + 1) % [_imgUrls count];
} else {
_pageControl.currentPage = _showIndex;
}
}
}
#pragma mark - 設(shè)置當前顯示的圖片
- (void)setShowIndex:(NSInteger)showIndex {
_showIndex = showIndex;
// 不管向左滑還是向右滑,這里立即將將要顯示的圖片展示在中間的 ImgView 上,然后馬上將 ScrollView 偏移量設(shè)置到顯示中間的 ImgView
// 由于代碼執(zhí)行及屏幕顯示渲染非???,肉眼幾乎看不出,所以這樣就達到了效果
[_leftImgView sd_setImageWithURL:[NSURL URLWithString:_imgUrls[(_showIndex + [_imgUrls count] - 1) % [_imgUrls count]]]];
[_middleImgView sd_setImageWithURL:[NSURL URLWithString:_imgUrls[showIndex]]];
[_rightImgView sd_setImageWithURL:[NSURL URLWithString:_imgUrls[(showIndex + 1) % [_imgUrls count]]]];
// 切記,這里設(shè)置 ScrollView 偏移量不能用動畫
[_scrollView setContentOffset:CGPointMake(CGRectGetWidth(self.bounds), 0)];
}
#pragma mark - 自動滾動時,計時器的暫停與啟動
/** 暫停計時器 */
- (void)pauseCycleRoll {
if (_cycleRollTimer) {
[_cycleRollTimer setFireDate:[NSDate distantFuture]];
}
}
/** 啟動定時器【在計時器暫停狀態(tài)下】 */
- (void)startCycleRoll {
if (_cycleRollTimer) {
[_cycleRollTimer setFireDate:[NSDate distantPast]];
}
}
#pragma mark - 停止計時器,并釋放
- (void)stopTimer {
if (_cycleRollTimer) {
[_cycleRollTimer invalidate];
_cycleRollTimer = nil;
}
}
#pragma mark - WTCycleRollView 釋放的時候 停止計時器,并釋放,否則會造成內(nèi)存泄漏
- (void)dealloc {
[self stopTimer];
}
@end