輪播圖的基本原理

最近接到了一個(gè)新的需求,是對(duì)以前app里的商城進(jìn)行一個(gè)簡(jiǎn)單的改版,具體的要求是商品列表做成一個(gè)輪播圖的形式,下面可以滑動(dòng),選中中間的商品并且進(jìn)行放大處理(如下圖所示)
效果展示.gif

通過看網(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í)的大小。如圖所示


輪播圖.png

首先上.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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容