前言:对于banner轮播图,相信大家都会经常用到。昨天下午看到自己之前用UIScrollView
定制的banner轮播图写的有点乱,就想着再封装一个整齐一点能够大众化使用的banner轮播图,于是昨晚用UICollectionView
又重新封装了一个,感觉用着还不错,今天给大家简单介绍一下,希望能够帮到有需要的朋友。
原理:使用UICollectionView
通过定时器不断改变UICollectionView
的偏移量来达到轮播滚动的效果,通过UIScrollView
的代理方法计算UICollectionView
偏移量并关联到UIPageControl
的currentPage
属性实现滚动的同时下方UIPageControl
的currentPage
跟着做出相应的改变。
下面给大家正式讲解如何来封装:
首先创建一个继承于UIView
的类RHBannerView
,在RHBannerView.h
中定义外漏的属性和方法以及用于传值的代理方法,如下:
#import
#import "RHBannerModel.h"
// pageControl 所在位置
typedef NS_ENUM (NSInteger, RHBannerViewPageStyle) {
RHBannerViewPageStyleLeft = 0, // 左边
RHBannerViewPageStyleMiddle, // 中间
RHBannerViewPageStyleRight // 右边
};
@protocol RHBannerViewDelegate;
@interface RHBannerView : UIView
// 代理
@property (nonatomic, weak) id delegate;
// PageControl所在位置样式 默认靠右
@property (nonatomic, assign) RHBannerViewPageStyle pageStyle;
// PageControl未选中圆点颜色 默认灰色
@property (nonatomic, strong) UIColor * pageTintColor;
// PageControl选中圆点颜色 默认白色
@property (nonatomic, strong) UIColor * currentPageTintColor;
// 轮播间隔时间 默认3秒
@property (nonatomic, assign) NSTimeInterval intervalTime;
// 定义构造方法快速创建对象
- (instancetype)initWithModels:(NSArray *)models;
// 定义构造方法快速创建对象
- (instancetype)initWithFrame:(CGRect)frame models:(NSArray *)models;
/**
根据model配置bannerView
@param models 存储RHBannerModel对象的数组
*/
- (void)configBannerWithModels:(NSArray *)models;
@end
@protocol RHBannerViewDelegate
@optional
// 点击图片
- (void)bannerView:(RHBannerView *)bannerView didSelectAtIndex:(NSInteger)index;
@end
这里定义了一个枚举用来设置下方UIPageControl
所在的位置,定义了一个代理方法用来传递当前点击的banner图是哪一个,还定制了一个RHBannerModel
来存储banner图的信息,这个model
大家可以根据自己的需求做出相应的改动。
外漏的方法中有两个是定制的初始化构造方法,可以直接传入banner模型信息的数组,不需要再次调用配置banner模型信息的对象方法;如果初始化方法使用了没有传入banner模型信息的init
或者initWithFrame
,那么后边需要调用配置banner模型信息的对象方法configBannerWithModels
。
下面我们来看看在RHBannerView.m
里边的实现如下:
#import "RHBannerView.h"
#import "RHBannerCell.h"
#define Cell_Collection @"Cell_Collection"
@interface RHBannerView ()
@property (nonatomic, strong) UICollectionView * collection;
@property (nonatomic, strong) UIPageControl * pageControl;
@property (nonatomic, strong) NSTimer * timer;
@property (nonatomic, strong) NSMutableArray * dataArr;
@property (nonatomic, assign) NSInteger page;
@end
@implementation RHBannerView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_page = 1;
_intervalTime = 3;
[self addSubviews];
self.pageStyle = RHBannerViewPageStyleRight;
self.backgroundColor = [UIColor yellowColor];
}
return self;
}
- (instancetype)initWithModels:(NSArray *)models {
self = [super init];
if (self) {
[self configBannerWithModels:models];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame models:(NSArray *)models {
self = [super initWithFrame:frame];
if (self) {
_page = 1;
_intervalTime = 3;
[self addSubviews];
self.pageStyle = RHBannerViewPageStyleRight;
self.backgroundColor = [UIColor yellowColor];
[self configBannerWithModels:models];
}
return self;
}
- (void)addSubviews {
[self addSubview:self.collection];
[self addSubview:self.pageControl];
}
- (void)layoutSubviews {
[super layoutSubviews];
_collection.frame = self.bounds;
[self makeConstraintForPageControlWithPageStyle:self.pageStyle];
if (self.dataArr.count > 1) {
[_collection setContentOffset:CGPointMake(self.bounds.size.width, 0)];
}
[_collection reloadData];
}
#pragma mark - public
- (void)configBannerWithModels:(NSArray *)models {
[self.dataArr removeAllObjects];
[self removeTimer];
_pageControl.numberOfPages = 0;
if (models.count == 0) {
[_collection reloadData];
return;
}
if (models.count == 1) {
[self.dataArr addObjectsFromArray:models];
} else {
[self.dataArr addObject:models.lastObject];
[self.dataArr addObjectsFromArray:models];
[self.dataArr addObject:models.firstObject];
[_collection setContentOffset:CGPointMake(self.bounds.size.width, 0)];
_pageControl.numberOfPages = models.count;
[self makeConstraintForPageControlWithPageStyle:self.pageStyle];
[self addTimer];
}
[_collection reloadData];
}
- (void)run {
if (self.dataArr.count > 1) {
[self addTimer];
}
}
- (void)stop {
[self removeTimer];
}
#pragma mark - private
- (void)makeConstraintForPageControlWithPageStyle:(RHBannerViewPageStyle)pageStyle {
float interval = 15;
switch (pageStyle) {
case RHBannerViewPageStyleLeft:
_pageControl.frame = CGRectMake(10, self.bounds.size.height - 50, self.dataArr.count * interval, 50);
break;
case RHBannerViewPageStyleMiddle:
_pageControl.frame = CGRectMake((self.bounds.size.width - self.dataArr.count * interval) / 2 , self.bounds.size.height - 50, self.dataArr.count * interval, 50);
break;
case RHBannerViewPageStyleRight:
_pageControl.frame = CGRectMake(self.bounds.size.width - 10 - self.dataArr.count * interval, self.bounds.size.height - 50, self.dataArr.count * interval, 50);
break;
default:
break;
}
}
#pragma mark - timer
- (void)addTimer {
if (!self.timer) {
self.timer = [NSTimer scheduledTimerWithTimeInterval:self.intervalTime target:self selector:@selector(bannerRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
}
- (void)removeTimer {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
}
- (void)bannerRun {
_page++;
[self.collection setContentOffset:CGPointMake(_page * self.bounds.size.width, 0) animated:YES];
if (_page == self.dataArr.count - 1) {
_page = 1;
}
}
#pragma mark - collectionView delegate and dataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.dataArr.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
RHBannerCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:Cell_Collection forIndexPath:indexPath];
if (indexPath.row < self.dataArr.count) {
RHBannerModel * model = self.dataArr[indexPath.row];
[cell configCellWithImageUrl:model.picture placeholderImage:[UIImage imageNamed:model.placeholderName]];
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
NSInteger index = indexPath.row;
if (index == 0) {
index = self.dataArr.count - 3;
} else if (index == self.dataArr.count - 1) {
index = 0;
} else {
index -= 1;
}
if (self.delegate && [self.delegate respondsToSelector:@selector(bannerView:didSelectAtIndex:)]) {
[self.delegate bannerView:self didSelectAtIndex:index];
}
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.bounds.size;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == _collection) {
if (self.dataArr.count > 1) {
_page = _collection.contentOffset.x / self.bounds.size.width;
if (_collection.contentOffset.x == 0) {
_pageControl.currentPage = self.dataArr.count - 3;
[_collection setContentOffset:CGPointMake(self.bounds.size.width * (self.dataArr.count - 2), 0)];
} else if (_collection.contentOffset.x == self.bounds.size.width * (self.dataArr.count - 1)) {
_pageControl.currentPage = 0;
[_collection setContentOffset:CGPointMake(self.bounds.size.width, 0)];
} else if (_collection.contentOffset.x == self.bounds.size.width * _page) {
_pageControl.currentPage = _page - 1;
}
}
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self removeTimer];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self addTimer];
}
#pragma mark - setter and getter
- (UICollectionView *)collection {
if (!_collection) {
UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
UICollectionView * collection = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
collection.dataSource = self;
collection.delegate = self;
collection.bounces = NO;
collection.pagingEnabled = YES;
collection.showsHorizontalScrollIndicator = NO;
collection.backgroundColor = [UIColor whiteColor];
[collection registerClass:[RHBannerCell class] forCellWithReuseIdentifier:Cell_Collection];
_collection = collection;
}
return _collection;
}
- (UIPageControl *)pageControl {
if (!_pageControl) {
UIPageControl * pageControl = [[UIPageControl alloc] init];
pageControl.pageIndicatorTintColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.9];
pageControl.currentPageIndicatorTintColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9];
_pageControl = pageControl;
}
return _pageControl;
}
- (NSMutableArray *)dataArr {
if (!_dataArr) {
_dataArr = [NSMutableArray array];
}
return _dataArr;
}
- (void)setPageStyle:(RHBannerViewPageStyle)pageStyle {
_pageStyle = pageStyle;
[self makeConstraintForPageControlWithPageStyle:pageStyle];
}
- (void)setPageTintColor:(UIColor *)pageTintColor {
_pageTintColor = pageTintColor;
self.pageControl.pageIndicatorTintColor = pageTintColor;
}
- (void)setCurrentPageTintColor:(UIColor *)currentPageTintColor {
_currentPageTintColor = currentPageTintColor;
self.pageControl.currentPageIndicatorTintColor = currentPageTintColor;
}
- (void)setIntervalTime:(NSTimeInterval)intervalTime {
_intervalTime = intervalTime;
if (self.dataArr.count > 0) {
[self removeTimer];
[self addTimer];
}
}
@end
这里主要就是UICollectionView
和UIPageControl
的布局实现,在UICollectionView
的点击代理方法中进行RHBannerView
的代理回调。RHBannerCell
只有一个UIImageView
来放banner图片,其中使用到了第三方库SDWebImage
,有需求的朋友可以进行修改。
OK!到这里封装就结束了,我们来看一下如何使用:
#import "ViewController.h"
#import "RHBannerView.h"
@interface ViewController ()
@property (nonatomic, strong) RHBannerView * bannerView;
@property (nonatomic, strong) RHBannerView * bannerView2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.bannerView];
[self.view addSubview:self.bannerView2];
[self makeConstraintsForUI];
RHBannerModel * model1 = [[RHBannerModel alloc] init];
model1.placeholderName = @"001.jpg";
RHBannerModel * model2 = [[RHBannerModel alloc] init];
model2.placeholderName = @"002.jpg";
RHBannerModel * model3 = [[RHBannerModel alloc] init];
model3.placeholderName = @"003.jpg";
RHBannerModel * model4 = [[RHBannerModel alloc] init];
model4.placeholderName = @"004.jpg";
RHBannerModel * model5 = [[RHBannerModel alloc] init];
model5.placeholderName = @"005.jpg";
[_bannerView configBannerWithModels:@[model1, model2, model3, model4, model5]];
}
- (void)makeConstraintsForUI {
[_bannerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(@0);
make.top.mas_equalTo(@0);
make.right.mas_equalTo(@0);
make.height.mas_equalTo(_bannerView.mas_width).multipliedBy(9/16.0);
}];
}
#pragma mark - bannerView delegate
- (void)bannerView:(RHBannerView *)bannerView didSelectAtIndex:(NSInteger)index {
if (bannerView == _bannerView) {
NSLog(@"点击了上边bannerView第%@个图片", @(index));
} else {
NSLog(@"点击了下边bannerView2第%@个图片", @(index));
}
}
- (RHBannerView *)bannerView {
if (!_bannerView) {
RHBannerView * bannerView = [[RHBannerView alloc] init];
bannerView.pageStyle = RHBannerViewPageStyleMiddle;
bannerView.delegate = self;
bannerView.pageTintColor = [UIColor lightGrayColor];
bannerView.currentPageTintColor = [UIColor redColor];
bannerView.intervalTime = 2;
_bannerView = bannerView;
}
return _bannerView;
}
- (RHBannerView *)bannerView2 {
if (!_bannerView2) {
RHBannerModel * model1 = [[RHBannerModel alloc] init];
model1.placeholderName = @"001.jpg";
RHBannerModel * model2 = [[RHBannerModel alloc] init];
model2.placeholderName = @"002.jpg";
RHBannerModel * model3 = [[RHBannerModel alloc] init];
model3.placeholderName = @"003.jpg";
RHBannerModel * model4 = [[RHBannerModel alloc] init];
model4.placeholderName = @"004.jpg";
RHBannerModel * model5 = [[RHBannerModel alloc] init];
model5.placeholderName = @"005.jpg";
RHBannerView * bannerView = [[RHBannerView alloc] initWithFrame:CGRectMake(0, 300, self.view.bounds.size.width, 200) models:@[model1, model2, model3, model4, model5]];
bannerView.delegate = self;
bannerView.intervalTime = 3;
_bannerView2 = bannerView;
}
return _bannerView2;
}
@end
大家可以看到使用起来无论是初始化创建的时候添加约束传入banner图片信息,还是后期添加约束传入banner图片信息都非常简单。下面给大家呈现一下效果图如下:
对了,demo已经上传到git上边,有需要的朋友可以前往下载查看,下载地址为:https://github.com/guorenhao/RHBannerView.git
最后还是希望能够帮助到有需要的猿友们,希望我们能够共同成长进步,愿我们能够在开发的道路上越走越远!谢谢!