
通過看網(wǎng)上的輪子,結(jié)果發(fā)現(xiàn)很多都不能滿足我們的需求,于是我決定自己寫一個(gè)滿足該項(xiàng)目的輪播圖。
基本原理是用scrollView上面放置5個(gè)button,只展示下標(biāo)為1,2,3的,左邊隱藏下標(biāo)為0的,右邊隱藏下標(biāo)為4的,用于在滑動(dòng)的時(shí)候展示準(zhǔn)備好的內(nèi)容。當(dāng)滑動(dòng)一個(gè)button的寬度時(shí),將scrollView復(fù)位到剛開始的狀態(tài),并且將button重新賦值。其中下標(biāo)為2的button大小始終為選中時(shí)的大小。如圖所示

首先上.h文件的代碼
#import <UIKit/UIKit.h>
typedef void(^SYPCarouselViewBlock)(NSInteger selectedIndex);
@interface SYPCarouselView : UIView
@property (copy, nonatomic) SYPCarouselViewBlock block;
/**
*
* 初始化控件
*
* @param frame frame
*
* @param array 圖片鏈接數(shù)組
*
* @param selectedIndex 選中button下標(biāo)
*
*/
- (instancetype)initWithFrame:(CGRect)frame withArray:(NSArray *)array withSelectedIndex:(NSInteger)selectedIndex;
@end
block用來(lái)傳回選中下標(biāo)
相對(duì)應(yīng)的.m文件
@interface SYPCarouselView () <UIScrollViewDelegate>
/** 數(shù)組 */
@property (strong, nonatomic) NSArray *array;
/** 滑動(dòng)視圖 */
@property (strong, nonatomic) UIScrollView *scrollView;
/** 當(dāng)前選中下標(biāo) */
@property (assign, nonatomic) NSInteger selectedIndex;
@end
@implementation SYPCarouselView
- (instancetype)initWithFrame:(CGRect)frame withArray:(NSArray *)array withSelectedIndex:(NSInteger)selectedIndex
{
self = [super initWithFrame:frame];
if (self) {
_array = array;
_selectedIndex = selectedIndex;
[self createView];
}
return self;
}
比較簡(jiǎn)單,對(duì)初始化的參數(shù)賦值給屬性。接下來(lái)是創(chuàng)建滑動(dòng)視圖
- (void)createView {
self.backgroundColor = [UIColor whiteColor];
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
//滑動(dòng)視圖的寬與中間放大按鈕的寬一致,為控件寬的三分之一,高度是控件的高度
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(width / 3, 0, width / 3, height)];
_scrollView.contentSize = CGSizeMake(width / 3 * 5, height);
//隱藏滾動(dòng)條
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
//禁止彈出和分頁(yè)
_scrollView.bounces = NO;
_scrollView.pagingEnabled = YES;
//保證在scrollView以外還能展示
_scrollView.clipsToBounds = NO;
_scrollView.delegate = self;
[self addSubview:_scrollView];
//創(chuàng)建按鈕組
for (NSInteger i = 0; i < 5; i++) {
UIButton *button = [[UIButton alloc] init];
//當(dāng)下標(biāo)為2時(shí),按鈕變大
if (i == 2) {
button.frame = CGRectMake(width / 3 * 2, 0, width / 3, height);
} else {
button.frame = CGRectMake(i * width / 3 + 10, height / width * 30, width / 3 - 20, height - height / width * 60);
}
[_scrollView addSubview:button];
button.tag = i;
[button addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
}
[self setImage];
//滑動(dòng)視圖默認(rèn)將index為2的按鈕顯示在屏幕中間
[_scrollView setContentOffset:CGPointMake(width / 3 * 2, 0) animated:NO];
}
其中_scrollView.clipsToBounds = YES;是scrollView外展示button所必須的
#pragma mark - 在scrollView外接收滑動(dòng)事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *view = [super hitTest:point withEvent:event];
if ([view isEqual:self]) {
for (UIView *subview in _scrollView.subviews) {
CGPoint offset = CGPointMake(point.x - _scrollView.frame.origin.x + _scrollView.contentOffset.x - subview.frame.origin.x,
point.y - _scrollView.frame.origin.y + _scrollView.contentOffset.y - subview.frame.origin.y);
if ((view = [subview hitTest:offset withEvent:event])) {
return view;
}
}
return _scrollView;
}
return view;
}
重寫hitTest:withEvent:方法,從而使scrollView之外可以接收到滑動(dòng)事件。接下來(lái)是滑動(dòng)事件
#pragma mark - UIScrollViewDelegate滑動(dòng)時(shí)調(diào)用
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat width = self.bounds.size.width;
//往正方向滑動(dòng)
if (scrollView.contentOffset.x >= width) {
_selectedIndex++;
//當(dāng)index越界,超出范圍,根據(jù)index的值來(lái)判斷循環(huán)起始index
while (_selectedIndex > _array.count - 1) {
//正方向,順序循環(huán)
if (_selectedIndex > _array.count - 1) {
_selectedIndex = _selectedIndex - _array.count;
}
}
//圖片賦值
[self setImage];
//重置scrollView位置
[scrollView setContentOffset:CGPointMake(width / 3 * 2, 0) animated:NO];
//回調(diào)當(dāng)前下標(biāo)
_block(_selectedIndex);
//往負(fù)方向滑動(dòng)
} else if (scrollView.contentOffset.x <= width / 3) {
_selectedIndex--;
//當(dāng)index越界,超出范圍,根據(jù)index的值來(lái)判斷循環(huán)起始index
while (_selectedIndex < 0) {
//負(fù)方向,倒序循環(huán)
if (_selectedIndex < 0) {
_selectedIndex = _array.count + _selectedIndex;
}
}
//圖片賦值
[self setImage];
//重置scrollView位置
[scrollView setContentOffset:CGPointMake(width / 3 * 2, 0) animated:NO];
//回調(diào)當(dāng)前下標(biāo)
_block(_selectedIndex);
}
}
輪播圖重要的地方就是這里,因?yàn)閟crollView不能循環(huán),當(dāng)滑到頭的時(shí)候,要根據(jù)滑動(dòng)方向來(lái)對(duì)index進(jìn)行處理,做出循環(huán)效果
#pragma mark - 按鈕點(diǎn)擊事件
- (void)buttonAction:(UIButton *)button {
CGFloat width = self.bounds.size.width;
switch (button.tag) {
case 1: {
[_scrollView setContentOffset:CGPointMake(width / 3, 0) animated:YES];
} break;
case 2: {
} break;
case 3: {
[_scrollView setContentOffset:CGPointMake(width, 0) animated:YES];
} break;
default:
break;
}
}
按鈕點(diǎn)擊事件,不多解釋
#pragma mark - 賦值
- (void)setImage {
for (NSInteger i = 0; i < _scrollView.subviews.count; i++) {
UIButton *button = _scrollView.subviews[i];
//由于中間按鈕index為2,根據(jù)選中的圖片index確定其他位置按鈕圖片的index
NSInteger index = _selectedIndex + i - 2;
//如果index越界,對(duì)index進(jìn)行處理,做出循環(huán)效果
while (index < 0 || index > _array.count - 1) {
if (index < 0) {
index = _array.count + index;
} else if (index > _array.count - 1) {
index = index - _array.count;
}
}
//圖片下載
UIImageView *imageManager = [[UIImageView alloc] init];
[self addSubview:imageManager];
[imageManager sd_setImageWithURL:_array[index] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (image) {
[button setBackgroundImage:image forState:UIControlStateNormal];
[imageManager removeFromSuperview];
} else {
[button setBackgroundImage:KNEW_HOLDER_IMAGE forState:UIControlStateNormal];
[imageManager removeFromSuperview];
}
}];
}
}
利用前面選中的下標(biāo),來(lái)確定其他button圖片的index,因?yàn)橛?個(gè)button,當(dāng)圖片少于5張或者index越界的時(shí)候,要對(duì)index進(jìn)行處理,做出循環(huán)效果。圖片下載用的第三方框架SDWebImage