前言

早在半年之前,做了一个O2O类型的iOS应用,当时我负责做一个走马灯的效果。需求是在首页添加一个广告位,有图片的轮播,并且还要配上标题文字,而且还有支持点击跳转到详情界面。很多app都有这种效果,其实走马灯效果这个称号是html开发中的叫法,对于iOS开发中的这种效果暂且还没有一个官方的称号,暂且在iOS中称这种效果为广播轮播吧,因为我觉得这个比较形象也通俗易懂,你要是说走马灯效果,估计很多iOS开发的工程师还不懂你的意思。

其实,一开始的时候并不怎么顺利,可能是半年之前水平有限吧,首先遇到的问题就是图片不能循环滑动,后来用了一个小小的算法,实现了能循环滑动。经过测试发现,如果网络比较慢,广告位会显示空白,后面就给图片加了一张默认图片,也就是占位图像。但是还是不完美,网络图片缓存到了本地,但是不会刷新,于是就加个来通知,当网络图片已经缓存到本地了,就通知广告位刷新,把占位图像换成网络图片,这是半年之前的解决办法。在这次的项目中,我又负责这个功能,其实有个已经很成熟的图片缓存框架了,就是SDWebImage,基本每个项目中都会用到,所以我这次做这个广告轮播的效果,就引入了这个类中的缓存图片的方法,一句代码就解决了。

基本的需求点:
1. 支持循环滚动;
2. 支持加定时器,自动滚动;
3. 支持自定义是否要显示标题;
4. 支持自定义pageControl的位置(左、中、右三种方式);
5. 支持自定义标题的位置(左、中、右三种方式);
6. 支付广告轮播图片的点击,可以点击跳转到你所指定的界面;
7.只有一张图片时,自动隐藏pageControl;

一、头文件中的声明

//
//  YCAdView.h
//  TestAdView
//
//  Created by 袁灿 on 15/9/14.
//  Copyright (c) 2015年 yuancan. All rights reserved.
//

#import <UIKit/UIKit.h>

//标题文字显示的方式
typedef NS_ENUM(NSInteger, AdViewTitleShowStyle)
{
    AdViewTitleShowStyleNone,
    AdViewTitleShowStyleLeft,
    AdViewTitleShowStyleCenter,
    AdViewTitleShowStyleRight,
};

//pageControl显示方式
typedef NS_ENUM(NSInteger, UIPageControlShowStyle)
{
    UIPageControlShowStyleNone,
    UIPageControlShowStyleLeft,
    UIPageControlShowStyleCenter,
    UIPageControlShowStyleRight,
};

@interface YCAdView : UIView <UIScrollViewDelegate>

@property (strong, nonatomic) UIScrollView *scrollView;

//点击图片的回调方法
@property (strong, nonatomic) void (^clickAdImage)(NSInteger index);

/**
 *  自定义广告轮播视图
 *
 *  @param frame            广告轮播视图位置及大小
 *  @param arrImage         轮播的图片
 *  @param arrTitle         轮播的标题(如不需要展示标题,传nil)
 *  @param placeholderImage 无网络图片时的默认图片
 *
 *  @return 广告轮播视图
 */
+ (id)initAdViewWithFrame:(CGRect)frame
                   images:(NSArray *)arrImage
                   titles:(NSArray*)arrTitle
         placeholderImage:(UIImage *)placeholderImage;

@end

二、m文件中的实现

#define kADView_Width       _scrollView.bounds.size.width       //广告的宽度
#define kADView_Height      _scrollView.bounds.size.height      //广告的高度

#define kTime   5.0f       //轮播的时间

#import "YCAdView.h"
#import "UIImageView+WebCache.h"

@interface YCAdView ()

@property (retain,nonatomic) UIImageView *leftImageView;
@property (retain,nonatomic) UIImageView *centerImageView;
@property (retain,nonatomic) UIImageView *rightImageView;

@property (retain,nonatomic) UILabel *labTitle;
@property (retain,nonatomic) UIImage *placeHolderImage;
@property (retain,nonatomic) NSTimer *timer;
@property (retain,nonatomic) UIPageControl *pageControl;

@property (nonatomic,assign) NSUInteger centerImageIndex;
@property (nonatomic,assign) NSUInteger leftImageIndex;
@property (nonatomic,assign) NSUInteger rightImageIndex;
@property (nonatomic,assign) NSUInteger currentPage;

@property (nonatomic,strong) NSArray *arrAdTitle;
@property (nonatomic,strong) NSMutableArray *arrImgUrl;

@end

@implementation YCAdView

+ (id)initAdViewWithFrame:(CGRect)frame
                   images:(NSArray *)arrImage
                   titles:(NSArray*)arrTitle
         placeholderImage:(UIImage *)placeholderImage
{
    if (arrImage.count == 0) {
        return nil;
    }

    YCAdView *ycAdview = [[YCAdView alloc] initWithFrame:frame];
    [ycAdview setAdViewImage:arrImage];
    [ycAdview setAdViewTitle:arrTitle withShowStyle:AdViewTitleShowStyleLeft];
    [ycAdview setPageControlShowStyle:UIPageControlShowStyleRight];
    ycAdview.placeHolderImage = placeholderImage;

    return ycAdview;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {

        _scrollView = [[UIScrollView alloc] initWithFrame:frame];
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.delegate = self;
        _scrollView.contentOffset = CGPointMake(kADView_Width, 0);
        _scrollView.contentSize = CGSizeMake(kADView_Width*3, kADView_Height);
        _scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
        _scrollView.pagingEnabled = YES;
        _scrollView.bounces = NO;
        [self addSubview:_scrollView];

        _leftImageView = [[UIImageView alloc] initWithFrame:CGRectMake(kADView_Width*0, 0, kADView_Width, kADView_Height)];
        _centerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(kADView_Width*1, 0, kADView_Width, kADView_Height)];
        _rightImageView = [[UIImageView alloc] initWithFrame:CGRectMake(kADView_Width*2, 0, kADView_Width, kADView_Height)];

        //添加图片点击事件
        _centerImageView.userInteractionEnabled = YES;
        [_centerImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAdImage)]];

        [_scrollView addSubview:_leftImageView];
        [_scrollView addSubview:_centerImageView];
        [_scrollView addSubview:_rightImageView];

    }
        return self;
}


//设置广告轮播的图片
- (void)setAdViewImage:(NSArray *)arrImage
{
    _leftImageIndex = arrImage.count - 1;
    _centerImageIndex = 0;
    _rightImageIndex = 1;

    if (arrImage.count == 1) {
        _scrollView.scrollEnabled = NO;
        _rightImageIndex = 0;
    }

    _arrImgUrl = [[NSMutableArray alloc] init];

    for (int i=0; i<arrImage.count; i++) {
        NSString *strImg = [arrImage objectAtIndex:i];
        NSURL *urlImg = [NSURL URLWithString:strImg];
        [_arrImgUrl addObject:urlImg];
    }

    [_leftImageView sd_setImageWithURL:_arrImgUrl[_leftImageIndex] placeholderImage:_placeHolderImage];
    [_centerImageView sd_setImageWithURL:_arrImgUrl[_centerImageIndex] placeholderImage:_placeHolderImage];
    [_rightImageView sd_setImageWithURL:_arrImgUrl[_rightImageIndex] placeholderImage:_placeHolderImage];

    //添加定时器
    _timer = [NSTimer scheduledTimerWithTimeInterval:kTime target:self selector:@selector(moveAdImage) userInfo:nil repeats:YES];
}

//设置广告轮播的标题
- (void)setAdViewTitle:(NSArray *)arrTitle withShowStyle:(AdViewTitleShowStyle)adViewTitleShowStyle
{
    if (arrTitle.count == 0 || adViewTitleShowStyle == AdViewTitleShowStyleNone) {
        return;
    }

    _arrAdTitle = [NSArray arrayWithArray:arrTitle];

    //灰色遮罩层
    UIView *grayView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_scrollView.frame)-30, kADView_Width, 30)];
    grayView.backgroundColor = [UIColor blackColor];
    grayView.alpha = 0.3;
    [self addSubview:grayView];

    _labTitle = [[UILabel alloc] initWithFrame:CGRectMake(10, 0, grayView.frame.size.width-10, 30)];
    [grayView addSubview:_labTitle];
    _labTitle.textColor = [UIColor whiteColor];
    _labTitle.text = arrTitle[_centerImageIndex];

    if (adViewTitleShowStyle == AdViewTitleShowStyleLeft) {
        _labTitle.textAlignment = NSTextAlignmentLeft;
    } else if (adViewTitleShowStyle == AdViewTitleShowStyleCenter) {
        _labTitle.textAlignment = NSTextAlignmentCenter;
    } else if (adViewTitleShowStyle == AdViewTitleShowStyleRight) {
        _labTitle.textAlignment = AdViewTitleShowStyleRight;
    }
}

//创建pageControl,设置其样式
- (void)setPageControlShowStyle:(UIPageControlShowStyle)pageControlShowStyle
{
    if (pageControlShowStyle == UIPageControlShowStyleNone || _arrImgUrl.count == 1) {
        return;
    }

    _pageControl = [[UIPageControl alloc] init];
    _pageControl.currentPage = 0;
    _pageControl.numberOfPages = _arrImgUrl.count;
    [self addSubview:_pageControl];

    if (pageControlShowStyle == UIPageControlShowStyleLeft) {
        _pageControl.frame = CGRectMake(0, kADView_Height-30, 20*_pageControl.numberOfPages, 30);
    } else if (pageControlShowStyle == UIPageControlShowStyleCenter) {
        _pageControl.frame = CGRectMake((kADView_Width-20*_pageControl.numberOfPages)/2, CGRectGetMaxY(_scrollView.frame)-30, 20*_pageControl.numberOfPages, 30);
    } else if (pageControlShowStyle == UIPageControlShowStyleRight) {
        _pageControl.frame = CGRectMake(kADView_Width-20*_pageControl.numberOfPages, CGRectGetMaxY(_scrollView.frame)-30, 20*_pageControl.numberOfPages, 30);
    }
}

//定时器自动滚动视图
- (void)moveAdImage
{
    _leftImageIndex = _leftImageIndex + 1;
    _centerImageIndex = _centerImageIndex + 1;
    _rightImageIndex = _rightImageIndex + 1;
    if (_leftImageIndex == _arrImgUrl.count) {
        _leftImageIndex = 0;
    }
    if (_centerImageIndex == _arrImgUrl.count) {
        _centerImageIndex = 0;
    }
    if (_rightImageIndex == _arrImgUrl.count) {
        _rightImageIndex = 0;
    }

    [_leftImageView sd_setImageWithURL:_arrImgUrl[_leftImageIndex] placeholderImage:_placeHolderImage];
    [_centerImageView sd_setImageWithURL:_arrImgUrl[_centerImageIndex] placeholderImage:_placeHolderImage];
    [_rightImageView sd_setImageWithURL:_arrImgUrl[_rightImageIndex] placeholderImage:_placeHolderImage];

    _pageControl.currentPage = _centerImageIndex;

    _labTitle.text = _arrAdTitle[_centerImageIndex];

    _scrollView.contentOffset = CGPointMake(kADView_Width, 0);
}

//点击广告轮播的图片
- (void)tapAdImage
{
    _clickAdImage(_centerImageIndex);
}

#pragma mark - UIScrollView Delegate

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if (_scrollView.contentOffset.x == 0) {
        _leftImageIndex = _leftImageIndex - 1;
        _centerImageIndex = _centerImageIndex - 1;
        _rightImageIndex = _rightImageIndex - 1;
        if (_leftImageIndex == -1) {
            _leftImageIndex = _arrImgUrl.count - 1;
        }
        if (_centerImageIndex == -1) {
            _centerImageIndex = _arrImgUrl.count - 1;
        }
        if (_rightImageIndex == - 1) {
            _rightImageIndex = _arrImgUrl.count - 1;
        }
    }else if (_scrollView.contentOffset.x == kADView_Width * 2) {
        _leftImageIndex = _leftImageIndex + 1;
        _centerImageIndex = _centerImageIndex + 1;
        _rightImageIndex = _rightImageIndex + 1;
        if (_leftImageIndex == _arrImgUrl.count) {
            _leftImageIndex = 0;
        }
        if (_centerImageIndex == _arrImgUrl.count) {
            _centerImageIndex = 0;
        }
        if (_rightImageIndex == _arrImgUrl.count) {
            _rightImageIndex = 0;
        }
    }else {
        return;
    }

    _pageControl.currentPage = _centerImageIndex;

    if (_arrAdTitle.count > 0) {
        _labTitle.text = _arrAdTitle[_centerImageIndex];
    }

    [_leftImageView sd_setImageWithURL:_arrImgUrl[_leftImageIndex] placeholderImage:_placeHolderImage];
    [_centerImageView sd_setImageWithURL:_arrImgUrl[_centerImageIndex] placeholderImage:_placeHolderImage];
    [_rightImageView sd_setImageWithURL:_arrImgUrl[_rightImageIndex] placeholderImage:_placeHolderImage];

    _scrollView.contentOffset = CGPointMake(kADView_Width, 0);

}

@end

三.如何调用

#import "ViewController.h"
#import "YCAdView.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSArray *arrTitle = @[@"1",@"2",@"3"];
    NSArray *arrImage = @[@"http://krdong.weixinmob.com/static/uploads/adminapp/zhengce/1_plots.jpg",
                          @"http://krdong.weixinmob.com/static/uploads/adminapp/zhengce/758BDBD576E7FD8B65FA003353D2904E.jpg",
                          @"http://krdong.weixinmob.com/static/uploads/adminapp/zhengce/Hydrangeas.jpg"];

    YCAdView *ycAdView = [YCAdView initAdViewWithFrame:CGRectMake(0, 20, self.view.frame.size.width, 300)
                                                images:arrImage
                                                titles:arrTitle
                                      placeholderImage:[UIImage imageNamed:@"123.jpeg"]];
    ycAdView.clickAdImage = ^(NSInteger index)
    {
        NSLog(@"点击了第%ld图片",index);
    };

    [self.view addSubview:ycAdView];

}

@end

四. 需完善的地方
基本上满足了一般广告轮播的所有需求,但是我发现从产品角度出发需要完善一个细节,就是在手动滑动scrollView时,需要暂停定时器,此篇博客中没有对此做处理。另外,如果一个界面用了广告轮播的效果,如果还要在同一个控制器中开启另一个定时器,一定要注意,两个定时器在同一个控制器中使用,需要做处理,不然会引起另外一个定时器失效。