去年做E4S電商平台APP( PS:平台垮了,沒上線,杯具-_-|| )的時候實作了一個Banner,起初沒想着要實作循環滾動的效果,後來業務提了個問題,要求首頁的banner能夠循環輪播,那時我就奈悶了,蘋果的scrollview好像是沒有循環滾動的吧(PS:那時還是個雛,搞IOS開發沒多久),拔光頭發還想不出,後來通過網絡搜尋,爬貼,各種跪拜...最後得出個點子,就是"n+2".
什麼是”n+2”呢,其實這個方法有點雞肋,别看上去名字好像挺高大上的,說到底就是…就是…第一張左邊添加最後一張,最後一張右邊添加第一張,這就是n+2.很雞肋吧,原理是很簡單,寫成代碼又是另一回事了,當然這裡沒有太過複雜的算法,但是如果封裝得好,感覺也是成就感滿滿的,呵呵!大笑
其實我知道有種方法更好,記得有個設計模式叫享元模式,用這個模式可以讓對象的重用性充分利用,這樣就能夠比較完美了,我想最好的方法應該莫過于此吧…(求大牛莫噴,放過小弟吧,行行好!),但是想到實用性與現實性,必竟banner數量是有限的,是以沒必要寫太複雜,簡單的東西多好!我就喜歡簡單的東西,有同感的親舉手啦!得意
好吧,不說太多廢話了,上代碼吧(這裡可能代碼寫得不怎樣,但為了照顧新手,也為了自已進步,是以放大碼了,讓大家批鬥吧).
首先是STBanner.h檔案,應該不用說都看得懂吧…
#import <UIKit/UIKit.h>
/** 點選回調*/
typedefvoid (^onSelectBlock) (NSInteger selectedIndex);
@interface STBanner :UIView
/**
* 初始化STBanner
*
* @param bannerArr 圖檔位址數組
* @param frame 位置大小
* @return STBanner
*/
+ (instancetype)bannerWithBannerArray:(NSArray *)bannerArr withFrame:(CGRect)frame onSelectBlock:(onSelectBlock)block;
/**
*銷毀方法
*/
- (void)dead;
@end
然後是STBanner.m檔案,這裡引用了檔案STPageControl.h這個類(這個後面補上),還有SDImage這個類庫:
//* 本循環滾動視圖是 由n+2原理實作的
//* 簡單來說就是 : 第一張左邊添加最後一張,最後一張右邊添加第一張,這就是n+2
#import "STBanner.h"
#import "STPageControl.h"
#import "UIImageView+WebCache.h"
#define TIMER_DURACTION f
#define kIsOutOfRange
@interface STBanner() <UIScrollViewDelegate>
/** 子滾動視圖*/
@property (nonatomic,strong) UIScrollView *scrollview;
/** 頁面訓示器*/
@property (nonatomic,strong) UIPageControl * pageControl;
/** 圖檔位址數組*/
@property (nonatomic,strong) NSArray *bannerArr;
/** 圖檔容器*/
@property (nonatomic,strong) NSMutableArray * containers;
/** 頁數(是n+2後的頁數,并非實際顯示的頁數)*/
@property (nonatomic,assign) int pageCount;
/** 是否手動拖動*/
@property (nonatomic,assign) BOOL isDragging;
/** 計時器*/
@property (nonatomic,strong) NSTimer *timer;
/** 點選圖檔的回調*/
@property (nonatomic,strong) onSelectBlock block;
@end
@implementation STBanner
#pragma mark - Initialize
- (id)initWithFrame:(CGRect)frame {
self = [superinitWithFrame:frame];
NSAssert(self,@"STBanner執行個體化錯誤");
[selfcommonInit];
return self;
}
- (void)commonInit {
[selfaddSubview:self.scrollview];
[selfaddSubview:self.pageControl];
UITapGestureRecognizer * tap = [[UITapGestureRecognizeralloc]
initWithTarget:self
action:@selector(bannerTap)];
[selfaddGestureRecognizer:tap];
self.userInteractionEnabled =YES;
}
+ (instancetype)bannerWithBannerArray:(NSArray *)bannerArr withFrame:(CGRect)frame onSelectBlock:(onSelectBlock)block
{
STBanner * banner = [[STBanneralloc]initWithFrame:frame];
banner.bannerArr = bannerArr?[NSArrayarrayWithArray:bannerArr]:nil ;
banner.pageCount = (int)bannerArr.count+;
banner.containers = [NSMutableArrayarray];
banner.block = block;
banner.scrollview.contentSize =
CGSizeMake((frame.size.width * bannerArr.count) + ( * frame.size.width),);
banner.scrollview.contentOffset =
CGPointMake(banner.frame.size.width,);
[banner.scrollviewsetScrollEnabled:bannerArr.count-];
[banner createContainers];
bannerArr.count - ? [banner.timersetFireDate:[NSDatedateWithTimeIntervalSinceNow:TIMER_DURACTION]]:NULL;
banner.pageControl.numberOfPages = banner.bannerArr.count ;
return banner;
}
- (void)createContainers {
//初始化圖檔容器
for (NSInteger i =; i < self.pageCount; i ++) {
UIImageView * iv = [[UIImageViewalloc]initWithFrame:
CGRectMake(self.frame.size.width * i,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height)];
[iv setBackgroundColor:[UIColorcolorWithWhite:falpha:f]];
[self.scrollviewaddSubview:iv];
[self.containersaddObject:iv];
/** 圖檔的加載*/
NSInteger index;
if ((i != self.pageCount -) && i)
{//除第一張和最後一張,都按順序加載圖檔
index = i-;
}
else
{
if (i == self.pageCount-)
{//最後一張用來顯示第一圖檔
index =;
}
else if(!i)
{//第一張用來顯示最後一張圖檔
index =self.bannerArr.count-;
}
}
[iv sd_setImageWithURL:[NSURLURLWithString:self.bannerArr[index]]placeholderImage:nil];
}
}
- (void)layoutSubviews {
[superlayoutSubviews];
self.scrollview.frame =self.frame;
self.pageControl.frame =CGRectMake(self.frame.origin.x,
self.frame.size.height-,
self.frame.size.width,
);
}
#pragma mark - Getters
- (UIScrollView *)scrollview {
if (!_scrollview) {
_scrollview = [[UIScrollViewalloc]init];
_scrollview.delegate =self;
_scrollview.pagingEnabled =YES ;
_scrollview.showsHorizontalScrollIndicator =NO ;
_scrollview.backgroundColor = [UIColorclearColor];
}
return_scrollview;
}
- (UIPageControl *)pageControl {
if (!_pageControl) {
_pageControl = [[STPageControlalloc]
initWithPageIndicatorImageName:@"pagecontrol-thumb-normal"
withCurrentPageIndicatorImageName:@"pagecontrol-thumb-selected"];
_pageControl.pageIndicatorTintColor = [UIColorclearColor];
_pageControl.currentPageIndicatorTintColor = [UIColorclearColor];
_pageControl.userInteractionEnabled =NO ;
}
return_pageControl;
}
- (NSMutableArray *)containers{
if (!_containers) {
_containers = [NSMutableArrayarrayWithCapacity:];
}
return_containers;
}
- (NSTimer *)timer{
if (!_timer) {
_timer = [NSTimerscheduledTimerWithTimeInterval:TIMER_DURACTIONtarget:selfselector:@selector(changePict)userInfo:nilrepeats:YES];
[_timersetFireDate:[NSDatedistantFuture]];
}
return _timer;
}
#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat x = scrollView.contentOffset.x ;
NSInteger index =round(x/[UIScreenmainScreen].bounds.size.width)-;
self.pageControl.currentPage = index ;
// 判斷是否手動控制(isTimeUp 的 NO 值 為手動)
_isDragging?[_timersetFireDate:[NSDatedateWithTimeIntervalSinceNow:TIMER_DURACTION]]:NULL;
if (x >self.scrollview.contentSize.width -self.frame.size.width)
{
[selfscrollToFirst];
}
else if(x <=)
{
[selfscrollToLast];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
_isDragging = YES;
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
_isDragging = NO;
CGFloat x = scrollView.contentOffset.x ;
if (x >self.scrollview.contentSize.width -self.frame.size.width *)
{
[selfscrollToFirst];
}elseif(x == )
{
[selfscrollToLast];
}
}
/** 輪播動畫*/
-(void)changePict {
CGFloat x = self.scrollview.contentOffset.x +self.frame.size.width;
[UIViewanimateKeyframesWithDuration:delay:options:UIViewKeyframeAnimationOptionAllowUserInteractionanimations:^ {
[self.scrollviewsetContentOffset:CGPointMake(x,)];
if (x >self.scrollview.contentSize.width -self.frame.size.width *)
{
self.pageControl.currentPage =;
}
}completion:^(BOOL finished) {
if (x >self.scrollview.contentSize.width -self.frame.size.width *)
{
[selfscrollToFirst];
}
}];
}
/** 跳到第一張圖檔*/
- (void)scrollToFirst{
[self.scrollviewsetContentOffset:CGPointMake(self.frame.size.width,)];
}
/** 跳到最後一張圖檔*/
- (void)scrollToLast{
[self.scrollviewsetContentOffset:CGPointMake(self.frame.size.width*self.bannerArr.count, )];
}
#pragma mark - Actions
-(void)bannerTap {
self.block&&self.bannerArr.count?
self.block(self.pageControl.currentPage):NULL;
}
- (void)dead {
[_pageControlremoveFromSuperview];
[_timerinvalidate];
[selfremoveFromSuperview];
}
然後這裡補上STPageControl.h
#import <UIKit/UIKit.h>
@interface STPageControl :UIPageControl
/**
* 初始化STPageControl
*
* @param pageIndicatorImageName
* @param currentPageIndicatorImageName
* @return STPageControl
*/
- (instancetype)initWithPageIndicatorImageName:(NSString *)pageIndicatorImageName withCurrentPageIndicatorImageName:(NSString *)currentPageIndicatorImageName;
@end
STPageControl.m
#import "STPageControl.h"
@interface STPageControl (){
UIImage * _activeImage;
UIImage *_inactiveImage;
}
@property (nonatomic ,strong)UIImage *activeImage;
@property (nonatomic,strong) UIImage *inactiveImage;
@end
@implementation STPageControl
#pragma mark - Initialize
- (instancetype)initWithPageIndicatorImageName:(NSString *)pageIndicatorImageName withCurrentPageIndicatorImageName:(NSString *)currentPageIndicatorImageName{
NSAssert(self = [superinit], @"STPageControl初始化錯誤!");
_activeImage = [UIImageimageNamed:currentPageIndicatorImageName];
_inactiveImage = [UIImageimageNamed:pageIndicatorImageName];
return self;
}
#pragma mark - Setters
- (void)setCurrentPage:(NSInteger)currentPage
{
[supersetCurrentPage:currentPage];
[selfupdateDots];
}
/** 替換pageControl小點*/
- (void)updateDots
{
for (int i =; i< [self.subviewscount]; i++)
{
UIView *view = [self.subviewsobjectAtIndex:i];
CGRect frame = view.frame;
view.backgroundColor = [UIColorclearColor];
frame.size.width =f;
frame.size.height =f;
view.layer.cornerRadius =f;
view.frame = frame;
if (!view.subviews.count) {
UIImageView *iv = [[UIImageViewalloc]initWithFrame:CGRectMake(,
,
frame.size.width,
frame.size.height)];
[viewaddSubview:iv];
}
UIImageView *iv = [view.subviewsfirstObject];
if (i == self.currentPage)
{
[ivsetImage:self.activeImage];
}
else
{
[ivsetImage:self.inactiveImage];
}
}
}
最後寫上ViewController裡的調用例子(很簡單,也許是我寫得好吧!安靜自嘲一下哈)
好,附上ViewController.m檔案内容
#import "ViewController.h"
#import "STBanner.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[superviewDidLoad];
NSArray *dataArray = @[
@"http://pic26.nipic.com/20130121/9475856_141716386357_2.jpg",
@"http://pic23.nipic.com/20120823/6854834_144345077352_2.jpg",
@"http://pic30.nipic.com/20130626/12977223_211915153000_2.jpg",
@"http://pic12.nipic.com/20110124/5375473_153252019000_2.jpg"
];
[self.viewaddSubview:[STBannerbannerWithBannerArray:dataArray
withFrame:CGRectMake(,
,
self.view.frame.size.width,
self.view.frame.size.width *f)
onSelectBlock:^(NSInteger selectedIndex)
{
NSLog(@"%ld",selectedIndex);
[selfshowAlertWithMessage:[NSStringstringWithFormat:@"%@",@(selectedIndex)]];
}]];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)showAlertWithMessage:(NSString *)message {
[[[UIAlertViewalloc]initWithTitle:nilmessage:message delegate:nilcancelButtonTitle:@"oklala"otherButtonTitles: nil]show];
}
好了,代碼到此結束,吐cao到此結束!睡覺