这里介绍的滑动横幅,即我们常说的Banner,也以广告插播、步骤引导、图文专题等形式存在于各种项目实战中,开发中也会经常碰到这种类型的控件,而苹果自带控件没有,为此有个可重用的类,通过实例化就能直接拿来用的,不需反复编写代码去实现对于程序员来说实在很有必要。本文的GYCBanner类就是为此需求而存在的干货,UIScrollView结合UIPageControl就可实现,只需向GYCBanner对象添加你想要展示出来的页面(包括文字、图片、控件等),GYCBanner按创建顺序生成相应页面,支持手指滑动、点击滑动以及自动播放模式。
----------------------------------------------------------------------------------我是分割线------------------------------------------------------------------------------
完成效果图
----------------------------------------------------------------------------------华丽分割线------------------------------------------------------------------------------
1. 创建GYCBanner类
- 打开Xcode,新建一个类,命名为GYCBanner,父类为UIView,使用ARC
- 打开GYCBanner.h文件,编写代码如下:
#import <UIKit/UIKit.h>
@interface GYCBanner : UIView
/**
* 添加页面
*
* @param view 视图
*/
- (void)addPageWithView:(UIView *)view;
/**
* 启动自动向左播放模式
*
* @param gap 时间间隔
*/
- (void)runAutomaticDisplayModeTowardsLeft:(NSTimeInterval)gap;
/**
* 启动自动向右播放模式
*
* @param gap 时间间隔
*/
- (void)runAutomaticDisplayModeTowardsRight:(NSTimeInterval)gap;
/**
* 取消自动播放
*/
- (void)cancelAutomaticDisplayMode;
@end
- 添加以下代码至GYCBanner.m文件中顶部
@interface GYCBanner () <UIScrollViewDelegate> {
UIScrollView *slideView;
UIPageControl *pageControl;
BOOL isControlled;
NSTimer *displayTimer;
}
@end
这里声明了4个变量,从上往下分别代表滑动视图、页面控制器、当前页面滑动是否由页面控制器发出指令(避免手指滑动与使用页面控制器滑动产生冲突)、当前展示页面的停留时间,接下来就开始对类GYCBanner的基本功能进行实现。
2. 开始实现
- 预先设置好滑动视图和页面控制器,方法如下:
- (void)setupDefaultSlide {
if (nil == slideView) {
slideView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
//根据slideView里子视图的个数和显示区长宽确定其背后内容的长宽
slideView.contentSize = CGSizeMake(self.frame.size.width*slideView.subviews.count, slideView.frame.size.height);
slideView.delegate = self;
//设置slideView一次滚动一页
slideView.pagingEnabled = YES;
slideView.showsHorizontalScrollIndicator = NO;
slideView.showsVerticalScrollIndicator = NO;
if (![slideView isDescendantOfView:self]) {
[self addSubview:slideView];
}
}
- (void)setupDefaultPageControl {
if (nil == pageControl) {
pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, self.frame.size.height-37, self.frame.size.width, 37)];
}
pageControl.numberOfPages = slideView.subviews.count;
pageControl.currentPage = 0;
//使用pageControl滑动会调用changedToSlideBanner方法
[pageControl addTarget:self action:@selector(changedToSlideBanner) forControlEvents:UIControlEventValueChanged];
if (![pageControl isDescendantOfView:self]) {
[self addSubview:pageControl];
}
}
然后添加如下方法:
- (void)changedToSlideBanner {
isControlled = YES;
int page = pageControl.currentPage;
CGRect frame = slideView.frame;
frame.origin.x = frame.size.width * page;
[slideView scrollRectToVisible:frame animated:YES];
}
- 实现GYCBanner页面的添加方法:
- (void)addPageWithView:(UIView *)view {
if (view != nil) {
[slideView addSubview:view];
[self setupDefaultSlide];
[self setupDefaultPageControl];
}
}
- 实现滑动视图的委托:
#pragma mark - scroll view delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat pageWidth = scrollView.frame.size.width;
//得到当前滚动到的页数,第一页值为0
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
if (!isControlled) {
pageControl.currentPage = page;
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
isControlled = NO;
}
- 添加自动播放,循环展示页面(暂不支持对页面进行循环滚动):
- (void)executeTheLeftSlideWithOnce {
int page = pageControl.currentPage;
page++;
if (page >= slideView.subviews.count) {
page = 0;
}
CGRect frame = slideView.frame;
frame.origin.x = frame.size.width * page;
[slideView scrollRectToVisible:frame animated:NO];
}
- (void)executeTheRightSlideWithOnce {
int page = pageControl.currentPage;
page--;
if (page < 0) {
page = slideView.subviews.count - 1;
}
CGRect frame = slideView.frame;
frame.origin.x = frame.size.width * page;
[slideView scrollRectToVisible:frame animated:NO];
}
- (void)runAutomaticDisplayModeTowardsLeft:(NSTimeInterval)gap {
if (displayTimer.isValid) {
[displayTimer invalidate];
}
displayTimer = [NSTimer scheduledTimerWithTimeInterval:gap target:self selector:@selector(executeTheRightSlideWithOnce) userInfo:nil repeats:YES];
}
- (void)runAutomaticDisplayModeTowardsRight:(NSTimeInterval)gap {
if (displayTimer.isValid) {
[displayTimer invalidate];
}
displayTimer = [NSTimer scheduledTimerWithTimeInterval:gap target:self selector:@selector(executeTheLeftSlideWithOnce) userInfo:nil repeats:YES];
}
- (void)cancelAutomaticDisplayMode {
[displayTimer invalidate];
}
前两种方法向左向右是针对滑动视图上一个和下一个页面,紧随其后的两种方法只是起到重复调用上两种方法的作用,但是播放方向左右针对的是用户直观上的左右,与滑动方向恰好相反,所以调用的方法是截然相反的。取消自动播放,仅仅只需要将之前的NSTimer对象displayTime设置为无效即可。
- 最后就是类的初始化方法:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setup];
}
return self;
}
- (void)awakeFromNib {
[self setup];
}
- (void)setup {
[self setupDefaultSlide];
[self setupDefaultPageControl];
}
3. 编译运行
完成以上内容后,我们既可以通过Interface Builder在xib文件完成GYCBanner的最初创建,也可使用代码来创建,值得注意的是实例化后记得使用addPageWithView来添加页面,切记不要用addSubview方法来向GYCBanner对象添加页面!
//使用xib
for (int i = 0; i < 5; i++) {
CGRect frame = self.banner1.frame;
UIView *page = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width * i, 0, frame.size.width, frame.size.height)];
page.backgroundColor = [[UIColor darkGrayColor] colorWithAlphaComponent:((5-i)*51)/255.0];
//添加页面
[self.banner1 addPageWithView:page];
}
//使用initWithFrame
GYCBanner *banner2 = [[GYCBanner alloc] initWithFrame:CGRectMake(0, 0, 240, 160)];
banner2.center = CGPointMake(CGRectGetMidX(self.view.bounds), 360);
for (int i = 0; i < 3; i++) {
CGRect frame = banner2.frame;
UIView *page = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width * i, 0, frame.size.width, frame.size.height)];
page.backgroundColor = [UIColor darkGrayColor];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, 30)];
label.center = CGPointMake(CGRectGetMidX(banner2.bounds), CGRectGetMidY(banner2.bounds));
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor whiteColor];
label.textAlignment = NSTextAlignmentCenter;
label.text = [NSString stringWithFormat:@"%d", i+1];
[page addSubview:label];
//添加页面
[banner2 addPageWithView:page];
}
[self.view addSubview:banner2];
----------------------------------------------------------------------------------我想说的是------------------------------------------------------------------------------
读者可以在学习消化完后,对本部分的内容进行一些扩展,比如添加纵向的滑动、更多公开的接口、滚动动画等等......
源码下载地址:
https://github.com/ganyuchuan/GYCBox,详见GYCBanner。