iOS Banner
Feature
- 支持多张图片
- 支持自动轮播
- 支持开启/关闭循环轮播
- 支持定义滚动方向
- 支持定义Page Controll位置
- 支持自定义自动轮播间隔
- 支持显示/隐藏close按钮
TODO
- 高度自适应?为空时为0
Q&A
基于什么实现
基于UIScroll实现, 开启pagingEnabled
可以实现整页滚动
- (UIScrollView *)scrollView{
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.backgroundColor = [UIColor clearColor];
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
}
return _scrollView;
}
Page Control 显示 (banner下面的小点)
- (id)initWithFrame:(CGRect)frame scrollDirection:(LKBannerViewScrollDirection)direction images:(NSArray *)images{
...
//****************** Page Control *********
[self setPageControlStyle:LKBannerViewPageStyle_Middle];
self.pageControl.numberOfPages = self.totalPage;
self.pageControl.currentPage = self.curPageIndex;
[self addSubview:self.pageControl];
}
- (UIPageControl *)pageControl{
if (!_pageControl) {
_pageControl = [[UIPageControl alloc] initWithFrame:CGRectNull];
_pageControl.currentPageIndicatorTintColor = [LKUIStandard k1Color];
_pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}
return _pageControl;
}
循环滚动
用三个Image View 循环显示,每次滚动后,重新排列和计算滚动位移
//向 Scroll View 添加 Image View
- (id)initWithFrame:(CGRect)frame scrollDirection:(LKBannerViewScrollDirection)direction images:(NSArray *)images{
...
for (NSInteger i = 0; i < 3; i++)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.scrollView.bounds];
imageView.userInteractionEnabled = YES;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.tag = Banner_StartTag+i;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[imageView addGestureRecognizer:singleTap];
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
imageView.frame = CGRectOffset(imageView.frame, self.scrollView.frame.size.width * i, 0);
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
imageView.frame = CGRectOffset(imageView.frame, 0, self.scrollView.frame.size.height * i);
}
[self.scrollView addSubview:imageView];
}
...
}
- (void)refreshScrollView{
NSArray *curImageUrls = [self getDisplayImagesWithPageIndex:self.curPageIndex];
for (NSInteger i = 0; i < 3; i++)
{
UIImageView *imageView = (UIImageView *)[self.scrollView viewWithTag:Banner_StartTag+i];
NSString *url = curImageUrls.count > i ? curImageUrls[i] : nil;
if (imageView && [imageView isKindOfClass:[UIImageView class]] && url){
[imageView sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil];
}
}
// 水平滚动
if (self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if (self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentOffset = CGPointMake(0, self.scrollView.frame.size.height);
}
self.pageControl.currentPage = self.curPageIndex;
}
#pragma mark - Scroll View Delegate
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
NSInteger x = aScrollView.contentOffset.x;
NSInteger y = aScrollView.contentOffset.y;
//NSLog(@"did x=%d y=%d", x, y);
//取消已加入的延迟线程
if (self.enableRolling)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(rollingScrollAction) object:nil];
}
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
// 往下翻一张
if (x >= 2 * self.scrollView.frame.size.width)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
}
if (x <= 0)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex-1];
[self refreshScrollView];
}
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
// 往下翻一张
if (y >= 2 * self.scrollView.frame.size.height)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
}
if (y <= 0)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex-1];
[self refreshScrollView];
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{
// 水平滚动
if (self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if (self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentOffset = CGPointMake(0, self.scrollView.frame.size.height);
}
if (self.enableRolling)
{
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
}
自动轮播
- (void)startRolling{
if (self.imagesArray.count <= 1) {
return;
}
[self stopRolling];
self.enableRolling = YES;
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
- (void)stopRolling{
self.enableRolling = NO;
//取消已加入的延迟线程
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(rollingScrollAction) object:nil];
}
- (void)rollingScrollAction
{
[UIView animateWithDuration:0.25 animations:^{
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape) {
self.scrollView.contentOffset = CGPointMake(1.99*self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait) {
self.scrollView.contentOffset = CGPointMake(0, 1.99*self.scrollView.frame.size.height);
}
} completion:^(BOOL finished) {
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
if (self.enableRolling) {
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
}];
}
回调
@protocol LKBannerViewDelegate <NSObject>
@optional
///点击图片
- (void)bannerView:(LKBannerView *)bannerView didSelectImageView:(NSInteger)index withURL:(NSString *)imageURL;
///点击关闭按钮
- (void)bannerViewdidClosed:(LKBannerView *)bannerView;
@end
- (void)handleTap:(UITapGestureRecognizer *)tap
{
if ([self.delegate respondsToSelector:@selector(bannerView:didSelectImageView:withURL:)])
{
[self.delegate bannerView:self didSelectImageView:self.curPageIndex withURL:[self.imagesArray objectAtIndex:self.curPageIndex]];
}
}
完整代码
//
// LKBannerView.h
//
// Created by Terry Zhang on 15/4/13.
// Copyright (c) 2015年 Terry. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger, LKBannerViewScrollDirection)
{
/// 水平滚动
LKBannerViewScrollDirection_Landscape,
/// 垂直滚动
LKBannerViewScrollDirection_Portait
};
///Page Control 位置
typedef NS_ENUM(NSInteger, LKBannerViewPageStyle)
{
LKBannerViewPageStyle_None,
LKBannerViewPageStyle_Left,
LKBannerViewPageStyle_Right,
LKBannerViewPageStyle_Middle
};
@protocol LKBannerViewDelegate;
@interface LKBannerView : UIView
// 存放所有需要滚动的图片URL NSString
@property (nonatomic, strong) NSArray *imagesArray;
// scrollView滚动的方向
@property (nonatomic, assign) LKBannerViewScrollDirection scrollDirection;
// 每条显示时间
@property (nonatomic, assign) NSTimeInterval rollingDelayTime;
@property (nonatomic, weak) id <LKBannerViewDelegate> delegate;
///请使用该函数初始化
- (id)initWithFrame:(CGRect)frame scrollDirection:(LKBannerViewScrollDirection)direction images:(NSArray *)images;
///重新设置 imageUrls
- (void)reloadBannerWithURLs:(NSArray *)imageUrls;
///设置 Banner 圆角显示
- (void)setSquare:(NSInteger)asquare;
///设置 Banner 样式,默认PageControl居中
- (void)setPageControlStyle:(LKBannerViewPageStyle)pageStyle;
///设置是否显示关闭按钮,默认不显示
- (void)showClose:(BOOL)show;
///开起自动滚动 , 默认不开始自动滚动,请手动开启
- (void)startRolling;
- (void)stopRolling;
@end
@protocol LKBannerViewDelegate <NSObject>
@optional
///点击图片
- (void)bannerView:(LKBannerView *)bannerView didSelectImageView:(NSInteger)index withURL:(NSString *)imageURL;
///点击关闭按钮
- (void)bannerViewdidClosed:(LKBannerView *)bannerView;
@end
//
// LKBannerView.m
//
// Created by Terry Zhang on 15/4/13.
// Copyright (c) 2015年 Terry. All rights reserved.
//
#import "LKBannerView.h"
#import <UIView+LSFrame.h>
#import <UIImageView+WebCache.h>
#define Banner_StartTag 1000
#define Banner_RollingDelayTime 4
@interface LKBannerView() <UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIButton *closeBtn;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, assign) BOOL enableRolling;
@property (nonatomic, assign) NSInteger totalPage;
@property (nonatomic, assign) NSInteger curPageIndex;
- (void)refreshScrollView;
- (NSInteger)getPageIndex:(NSInteger)index;
- (NSArray *)getDisplayImagesWithPageIndex:(NSInteger)pageIndex;
@end
@implementation LKBannerView
#pragma mark - Public Method
- (id)initWithFrame:(CGRect)frame scrollDirection:(LKBannerViewScrollDirection)direction images:(NSArray *)images{
self = [super initWithFrame:frame];
if (self) {
//****************** Property *********
self.imagesArray = [[NSArray alloc] initWithArray:images];
self.scrollDirection = direction;
self.totalPage = self.imagesArray.count;
self.curPageIndex = 0;
_rollingDelayTime = Banner_RollingDelayTime;
//****************** Scroll View *********
self.scrollView.scrollEnabled = self.totalPage != 1;
// 在水平方向滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 3,
self.scrollView.frame.size.height);
}
// 在垂直方向滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width,
self.scrollView.frame.size.height * 3);
}
//向 Scroll View 添加 Image View
for (NSInteger i = 0; i < 3; i++)
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.scrollView.bounds];
imageView.userInteractionEnabled = YES;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.tag = Banner_StartTag+i;
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[imageView addGestureRecognizer:singleTap];
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
imageView.frame = CGRectOffset(imageView.frame, self.scrollView.frame.size.width * i, 0);
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
imageView.frame = CGRectOffset(imageView.frame, 0, self.scrollView.frame.size.height * i);
}
[self.scrollView addSubview:imageView];
}
[self addSubview:self.scrollView];
//****************** Page Control *********
[self setPageControlStyle:LKBannerViewPageStyle_Middle];
self.pageControl.numberOfPages = self.totalPage;
self.pageControl.currentPage = self.curPageIndex;
[self addSubview:self.pageControl];
[self refreshScrollView];
}
return self;
}
- (void)reloadBannerWithURLs:(NSArray *)imageUrls{
self.imagesArray = [[NSArray alloc] initWithArray:imageUrls];
self.totalPage = self.imagesArray.count;
self.curPageIndex = 0;
self.pageControl.numberOfPages = self.totalPage;
self.pageControl.currentPage = self.curPageIndex;
self.scrollView.scrollEnabled = self.totalPage != 1;
[self refreshScrollView];
}
- (void)setSquare:(NSInteger)asquare{
if (self.scrollView)
{
self.scrollView.layer.cornerRadius = asquare;
if (asquare == 0)
{
self.scrollView.layer.masksToBounds = NO;
}
else
{
self.scrollView.layer.masksToBounds = YES;
}
}
}
- (void)setPageControlStyle:(LKBannerViewPageStyle)pageStyle{
if (pageStyle == LKBannerViewPageStyle_Left)
{
[self.pageControl setFrame:CGRectMake(5, self.bounds.size.height-15, 60, 15)];
}
else if (pageStyle == LKBannerViewPageStyle_Right)
{
[self.pageControl setFrame:CGRectMake(self.bounds.size.width-5-60, self.bounds.size.height-15, 60, 15)];
}
else if (pageStyle == LKBannerViewPageStyle_Middle)
{
[self.pageControl setFrame:CGRectMake((self.bounds.size.width-60)/2, self.bounds.size.height-15, 60, 15)];
}
else if (pageStyle == LKBannerViewPageStyle_None)
{
[self.pageControl setHidden:YES];
}
}
- (void)showClose:(BOOL)show{
self.closeBtn.hidden = !show;
}
#pragma mark Rolling
- (void)startRolling{
if (self.imagesArray.count <= 1) {
return;
}
[self stopRolling];
self.enableRolling = YES;
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
- (void)stopRolling{
self.enableRolling = NO;
//取消已加入的延迟线程
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(rollingScrollAction) object:nil];
}
- (void)rollingScrollAction
{
[UIView animateWithDuration:0.25 animations:^{
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentOffset = CGPointMake(1.99*self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentOffset = CGPointMake(0, 1.99*self.scrollView.frame.size.height);
}
} completion:^(BOOL finished) {
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
if (self.enableRolling)
{
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
}];
}
#pragma mark - Event
- (void)handleTap:(UITapGestureRecognizer *)tap
{
if ([self.delegate respondsToSelector:@selector(bannerView:didSelectImageView:withURL:)])
{
[self.delegate bannerView:self didSelectImageView:self.curPageIndex withURL:[self.imagesArray objectAtIndex:self.curPageIndex]];
}
}
- (void)closeBanner
{
[self stopRolling];
if ([self.delegate respondsToSelector:@selector(bannerViewdidClosed:)])
{
[self.delegate bannerViewdidClosed:self];
}
}
#pragma mark - Scroll View Delegate
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView
{
NSInteger x = aScrollView.contentOffset.x;
NSInteger y = aScrollView.contentOffset.y;
//NSLog(@"did x=%d y=%d", x, y);
//取消已加入的延迟线程
if (self.enableRolling)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(rollingScrollAction) object:nil];
}
// 水平滚动
if(self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
// 往下翻一张
if (x >= 2 * self.scrollView.frame.size.width)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
}
if (x <= 0)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex-1];
[self refreshScrollView];
}
}
// 垂直滚动
else if(self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
// 往下翻一张
if (y >= 2 * self.scrollView.frame.size.height)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex+1];
[self refreshScrollView];
}
if (y <= 0)
{
self.curPageIndex = [self getPageIndex:self.curPageIndex-1];
[self refreshScrollView];
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{
// 水平滚动
if (self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if (self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentOffset = CGPointMake(0, self.scrollView.frame.size.height);
}
if (self.enableRolling)
{
[self performSelector:@selector(rollingScrollAction) withObject:nil afterDelay:self.rollingDelayTime];
}
}
#pragma mark - Private Method
- (NSInteger)getPageIndex:(NSInteger)index{
if (index<0){
index = self.totalPage - 1;
}
if (index == self.totalPage)
{
index = 0;
}
return index;
}
- (NSArray *)getDisplayImagesWithPageIndex:(NSInteger)pageIndex{
NSInteger preIndex = [self getPageIndex:self.curPageIndex-1];
NSInteger nextIndex = [self getPageIndex:self.curPageIndex+1];
NSMutableArray *images = [NSMutableArray arrayWithCapacity:0];
if (self.imagesArray.count > preIndex) {
[images addObject:self.imagesArray[preIndex]];
}
if (self.imagesArray.count > self.curPageIndex) {
[images addObject:self.imagesArray[self.curPageIndex]];
}
if (self.imagesArray.count > nextIndex) {
[images addObject:self.imagesArray[nextIndex]];
}
return images;
}
- (void)refreshScrollView{
NSArray *curImageUrls = [self getDisplayImagesWithPageIndex:self.curPageIndex];
for (NSInteger i = 0; i < 3; i++)
{
UIImageView *imageView = (UIImageView *)[self.scrollView viewWithTag:Banner_StartTag+i];
NSString *url = curImageUrls.count > i ? curImageUrls[i] : nil;
if (imageView && [imageView isKindOfClass:[UIImageView class]] && url)
{
[imageView sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil];
}
}
// 水平滚动
if (self.scrollDirection == LKBannerViewScrollDirection_Landscape)
{
self.scrollView.contentOffset = CGPointMake(self.scrollView.frame.size.width, 0);
}
// 垂直滚动
else if (self.scrollDirection == LKBannerViewScrollDirection_Portait)
{
self.scrollView.contentOffset = CGPointMake(0, self.scrollView.frame.size.height);
}
self.pageControl.currentPage = self.curPageIndex;
}
#pragma mark - Init Property
- (UIScrollView *)scrollView{
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.backgroundColor = [UIColor clearColor];
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
}
return _scrollView;
}
- (UIPageControl *)pageControl{
if (!_pageControl) {
_pageControl = [[UIPageControl alloc] initWithFrame:CGRectNull];
_pageControl.currentPageIndicatorTintColor = [LKUIStandard k1Color];
_pageControl.pageIndicatorTintColor = [UIColor whiteColor];
}
return _pageControl;
}
- (UIButton *)closeBtn{
if (!_closeBtn)
{
_closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeBtn setFrame:CGRectMake(self.bounds.size.width-40, 0, 40, 40)];
[_closeBtn setContentVerticalAlignment:UIControlContentVerticalAlignmentTop];
[_closeBtn setContentHorizontalAlignment:UIControlContentHorizontalAlignmentRight];
[_closeBtn addTarget:self action:@selector(closeBanner) forControlEvents:UIControlEventTouchUpInside];
[_closeBtn setImage:[UIImage imageNamed:@"banner_close"] forState:UIControlStateNormal];
[self addSubview:_closeBtn];
}
return _closeBtn;
}
@end