iOS开发-自定制banner轮播图

banner.png

前言:对于banner轮播图,相信大家都会经常用到。昨天下午看到自己之前用UIScrollView定制的banner轮播图写的有点乱,就想着再封装一个整齐一点能够大众化使用的banner轮播图,于是昨晚用UICollectionView又重新封装了一个,感觉用着还不错,今天给大家简单介绍一下,希望能够帮到有需要的朋友。

原理:使用UICollectionView通过定时器不断改变UICollectionView的偏移量来达到轮播滚动的效果,通过UIScrollView的代理方法计算UICollectionView偏移量并关联到UIPageControlcurrentPage属性实现滚动的同时下方UIPageControlcurrentPage跟着做出相应的改变。

下面给大家正式讲解如何来封装:
首先创建一个继承于UIView的类RHBannerView,在RHBannerView.h中定义外漏的属性和方法以及用于传值的代理方法,如下:

#import <UIKit/UIKit.h>
#import "RHBannerModel.h"

// pageControl 所在位置
typedef NS_ENUM (NSInteger, RHBannerViewPageStyle) {
    
    RHBannerViewPageStyleLeft = 0,  // 左边
    RHBannerViewPageStyleMiddle,    // 中间
    RHBannerViewPageStyleRight      // 右边
};

@protocol RHBannerViewDelegate;
@interface RHBannerView : UIView
// 代理
@property (nonatomic, weak) id<RHBannerViewDelegate> 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<RHBannerModel *> *)models;
// 定义构造方法快速创建对象
- (instancetype)initWithFrame:(CGRect)frame models:(NSArray<RHBannerModel *> *)models;

/**
 根据model配置bannerView

 @param models 存储RHBannerModel对象的数组
 */
- (void)configBannerWithModels:(NSArray<RHBannerModel *> *)models;

@end
@protocol RHBannerViewDelegate <NSObject>

@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 () <UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@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<RHBannerModel *> *)models {
    
    self = [super init];
    
    if (self) {
        
        [self configBannerWithModels:models];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame models:(NSArray<RHBannerModel *> *)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<RHBannerModel *> *)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

这里主要就是UICollectionViewUIPageControl的布局实现,在UICollectionView的点击代理方法中进行RHBannerView的代理回调。RHBannerCell只有一个UIImageView来放banner图片,其中使用到了第三方库SDWebImage,有需求的朋友可以进行修改。

OK!到这里封装就结束了,我们来看一下如何使用:

#import "ViewController.h"
#import "RHBannerView.h"

@interface ViewController () <RHBannerViewDelegate>

@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图片信息都非常简单。下面给大家呈现一下效果图如下:


banner.gif

对了,demo已经上传到git上边,有需要的朋友可以前往下载查看,下载地址为:https://github.com/guorenhao/RHBannerView.git

最后还是希望能够帮助到有需要的猿友们,希望我们能够共同成长进步,愿我们能够在开发的道路上越走越远!谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,266评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 早上12点位于乌鲁木齐的一家家装司公里,三人正围坐电脑跟前谈论家装细节,突然一通电话打到了设计师林巧的手机上。她侧...
    努力红阅读 251评论 0 0
  • 功能:1.提供接口,直接调用添加:左视图,标题,以及右视图 2.closure(闭包)提供回调方法,监听点击事件 ...
    1693c7e88afe阅读 2,375评论 0 1
  • 在法律文件中通常包括定义部分,常见的中文表述有:“……的意思是……”,“(所称)……是指……”,我们常常翻译成“S...
    六十度冰阅读 1,312评论 0 50