UIScrollView實(shí)現(xiàn)循環(huán)輪播Banner(自定義Cell實(shí)現(xiàn)復(fù)用方案)

效果圖

實(shí)現(xiàn)循環(huán)輪播圖的各種方案

  • 輪播圖的實(shí)現(xiàn)方案有很多種,大體上分為CollectionView和ScrollView實(shí)現(xiàn)的兩個(gè)方向。其中CollectionView實(shí)現(xiàn)的方案較多利用了CollectionView的特性,實(shí)現(xiàn)比較簡單,但是比較受限于CollectionView的這個(gè)框架,而且出于學(xué)習(xí)的目的出發(fā)會(huì)比較不夠深入。ScrollView實(shí)現(xiàn)的比較大眾比較令人滿意的方案是使用三個(gè)UIImageView實(shí)現(xiàn)輪播效果。原理這里有位同學(xué)有提到,iOS無限輪播圖片的兩種方式,就不贅述啦。然后其實(shí)使用兩個(gè)UIImageView也是可以實(shí)現(xiàn)的。但是不管是使用兩個(gè)還是三個(gè)UIImageView,都是只限于可見范圍內(nèi)只存在一個(gè)banner的情況,如果可見范圍內(nèi)有n個(gè)banner,就需要n+2個(gè)UIImageView。所以這種方法還是不夠靈活,看看有沒有其他方案。

  • 本文描述的方案是通過UIScrollView實(shí)現(xiàn)Cell的重用機(jī)制,然后實(shí)現(xiàn)循環(huán)輪播的功能。這樣既可以解決使用2或者3個(gè)UIImageView的不靈活的問題,也可以突破UICollectionView框架的限制,提高組件的可擴(kuò)展性。

如何實(shí)現(xiàn)

用ScrollView實(shí)現(xiàn)類似CollectionView的Cell重用機(jī)制
  • 首先為什么要用ScrollView去實(shí)現(xiàn)Cell的重用機(jī)制呢。除了出于學(xué)習(xí)的角度考慮之外,我們知道Cell的重用機(jī)制其實(shí)就是建立一個(gè)Cell的復(fù)用池,當(dāng)可見的Cell滑動(dòng)出屏幕外的時(shí)候?qū)⑵浠厥?,下一個(gè)Cell將要顯示于屏幕上時(shí)從復(fù)用池中拿一個(gè)Cell進(jìn)行復(fù)用,如果沒有就new一個(gè)。所以,假如靜止時(shí)可見范圍內(nèi)只存在一個(gè)banner頁的話,那么最多滑動(dòng)時(shí)可見的就是兩個(gè),一共只需要new兩個(gè)Cell出來。
  1. 我們先用一個(gè)繼承于UIView的View來承載這個(gè)banner組件,叫KiraBanner。它的結(jié)構(gòu)很簡單,一個(gè)UIScrollView用于滑動(dòng),一個(gè)Cell的集合作為Cell的重用池,一個(gè)PageControl。然后我們在init方法中進(jìn)行初始化。當(dāng)然,在init方法中需要對一些KiraBanner的一些默認(rèn)屬性進(jìn)行初始化設(shè)置,如isCircle(是否循環(huán))、topBottomSpace(上下邊距)、leftRightSpace(左右邊距)等等。
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) NSMutableSet *reuseCells;
@property (nonatomic,retain)  UIPageControl *pageControl;
- (instancetype)init {
    self = [super init];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (void)commonInit {
    self.clipsToBounds = YES;
    self.pageCount = 0;
    self.isAutoScroll = YES;
    
    //默認(rèn)左右間距為20,上下間距為30
    self.leftRightSpace = 20;
    self.topBottomSpace = 30;
    _currentIndex = 0;
    
    //默認(rèn)自動(dòng)滾動(dòng)時(shí)間間隔為5s
    _autoTime = 5.0;
    _visibleRange = NSMakeRange(0, 0);
    self.reuseCells = [[NSMutableSet alloc] init];
    
    self.scrollView.scrollsToTop = NO;
    self.scrollView.clipsToBounds = NO;
    self.scrollView.showsVerticalScrollIndicator = NO;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.backgroundColor = [UIColor redColor];
    _currentIndex = 0;
    [self.scrollView setFrame:self.bounds];
    [self addSubview:self.scrollView];
}

2.仿照CollectionView的使用,我們需要兩個(gè)委托方法來告訴KiraBanner中banner的數(shù)量以及每個(gè)banner展示的內(nèi)容是什么,也就是KiraBanner的DataSource。

@protocol KiraBannerDataSource <NSObject>
@required
/**
 *  設(shè)置banner的數(shù)量
 */
- (NSInteger)numberOfItemsInKiraBanner:(KiraBanner *)banner;

/**
 *  設(shè)置某一頁banner的內(nèi)容
 */
- (UIView *)kiraBanner: (KiraBanner *)banner viewForItemAtIndex:(NSInteger)index;

@optional

@end

3.然后我們在KiraBanner中增加一個(gè)Class的屬性,對外提供regiseterClassForCells的方法來注冊Cell。

/**
 *  注冊的cell類型
 */
@property (nonatomic, strong) Class cellClass;
- (void)regiseterClassForCells: (Class) cellClass {
    self.cellClass = cellClass;
}

4.dequeueReusableCell 方法拿到可復(fù)用Cell,也就是即將進(jìn)入屏幕中的cell的來源

- (UIView *)dequeueReusableCell {
    UIView *cell = [self.reuseCells anyObject];
    if (cell) {
        [self.reuseCells removeObject:cell];
        NSLog(@"add a cell");
    }
    if (!cell) {
        cell = [[self.cellClass alloc] init];
        NSLog(@"produce a new cell");
    }
    return  cell;
}
  1. recycleCell方法,用于滑動(dòng)時(shí)將消失于屏幕外的cell回收。
- (void)recycleCell: (UIView *)cell {
    [self.reuseCells addObject:cell];
    [cell removeFromSuperview];
}

6、我們將每頁banner的size作為一個(gè)委托方法交給外部設(shè)置,同時(shí)增加私有屬性pageSize。包括banner的點(diǎn)擊等方法,都需要作為代理給外部調(diào)用。

@protocol KiraBannerDelegate <UIScrollViewDelegate>

/**
 *  設(shè)置一個(gè)page的size
 */
- (CGSize)sizeForPageInKiraBanner:(KiraBanner *)banner;

/**
 *  當(dāng)前banner滾動(dòng)到了哪一頁
 */
- (void)didScrollToIndex:(NSInteger)index inKiraBanner:(KiraBanner *)banner;

/**
 *  點(diǎn)擊某個(gè)cell
 */
- (void)didSelectCell:(UIView *)cell inKiraBannerAtIndex:(NSInteger)index;

/**
 *  當(dāng)前page滾動(dòng)過了整頁的百分比
 */
- (void)didScrollPercent:(float)percent OfPageInScrollView:(UIScrollView *)scrollView;

@end

7、在外部ViewController中簡單配置使用一下KiraBanner,并實(shí)現(xiàn)KiraBannerDataSource和Delegate的委托方法。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.dataArray = @[@"1.jpg",@"2.jpg",@"3.jpg",@"4.jpg"];
    self.banner = [[KiraBanner alloc] initWithFrame:CGRectMake(0, 72, Width, Width * 9 / 16)];
    [self.view addSubview:self.banner];
    self.banner.backgroundColor = [UIColor blackColor];
    self.banner.isCircle = YES;
    self.banner.leftRightSpace = 50;
    self.banner.topBottomSpace = 30;
    self.banner.clipsToBounds = YES;
    self.automaticallyAdjustsScrollViewInsets = NO;
    [self.banner regiseterClassForCells:[UIImageView class]];
    self.banner.bannerType = KiraBannerTypeHorizontal;
    self.banner.minimumPageAlpha = 1;
    self.banner.dataSource = self;
    self.banner.delegate = self;
}
- (UIView *)kiraBanner:(KiraBanner *)banner viewForItemAtIndex:(NSInteger)index {
    UIImageView *cell = (UIImageView *)[self.banner dequeueReusableCell];
    cell.image = [UIImage imageNamed:self.dataArray[index]];
    [cell setContentMode:UIViewContentModeScaleAspectFill];
    return cell;
}

- (CGSize)sizeForPageInKiraBanner:(KiraBanner *)banner {
    return CGSizeMake(Width - 60, (Width - 60) * 9 / 16);
}

- (NSInteger)numberOfItemsInKiraBanner:(KiraBanner *)banner {
    return self.dataArray.count;
}

- (void)didSelectCell:(UIView *)cell inKiraBannerAtIndex:(NSInteger)index {
    NSLog(@"banner of index : %ld is clicked.",(long)index);
}
  1. 前置項(xiàng)已經(jīng)準(zhǔn)備好了,接下來就是在didScrollView的代理方法中去做cell復(fù)用的邏輯。這里封裝一個(gè)方法setVisibleCellsAtContentOffset在didScrollView中調(diào)用,顧名思義就是根據(jù)contentoffset添加可見cell。那怎么做復(fù)用的邏輯呢。首先我們遍歷scrollView中的每個(gè)子Cell,根據(jù)cell的frame.origin.x(拿橫向滑動(dòng)舉例)和frame.size.width計(jì)算并與contentoffset作比較,判斷該cell是否在顯示區(qū)域外,如果是,則調(diào)用recycleCell方法對cell進(jìn)行回收。如果不是,則調(diào)用fillPageAtIndex將cell添加到scrollView上。
- (void)setVisibleCellsAtContentOffset:(CGPoint)offset {
    
    CGPoint startPoint = CGPointMake(offset.x - _scrollView.frame.origin.x, offset.y - _scrollView.frame.origin.y);
    CGPoint endPoint = CGPointMake(startPoint.x + self.bounds.size.width, startPoint.y + self.bounds.size.height);
    
    switch (self.bannerType) {
        case KiraBannerTypeHorizontal: {
            
            for (UIView *cellView in [self cellSubView]) {
                if (cellView.frame.origin.x + cellView.frame.size.width < startPoint.x) {
                    [self recycleCell:cellView];
                }
                if (cellView.frame.origin.x > endPoint.x) {
                    [self recycleCell:cellView];
                }
            }
            
            NSInteger startIndex = MAX(0, floor(startPoint.x / _pageSize.width));
            NSInteger endIndex = MIN(_pageCount, ceil(endPoint.x / _pageSize.width));
            
            _visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);
        
            for (NSInteger i = startIndex; i < endIndex ; i++) {
                [self fillPageAtIndex:i];
            }
        }
            break;
        case KiraBannerTypeVertical: {
            
            for (UIView *cellView in [self cellSubView]) {
                if (cellView.frame.origin.y + cellView.frame.size.height < startPoint.y) {
                    [self recycleCell:cellView];
                }
                if (cellView.frame.origin.y > endPoint.y) {
                    [self recycleCell:cellView];
                }
            }
            
            NSInteger startIndex = MAX(0, floor(startPoint.y / _pageSize.height));
            NSInteger endIndex = MIN(_pageCount, ceil(endPoint.y / _pageSize.height));
            //visibleRange表示可見的banner的index范圍
            _visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);
            
            for (NSInteger i = startIndex; i < endIndex ; i++) {
                [self fillPageAtIndex:i];
            }
        }
            break;
        default:
            break;
    }
    
}

- (NSArray *) cellSubView {
    NSMutableArray * cells = [[NSMutableArray alloc] init];
    for (UIView *subView in self.scrollView.subviews) {
        if ([subView isKindOfClass:[_cellClass class]]) {
            [cells addObject:subView];
        }
    }
    return [cells copy];
}

- (void)fillPageAtIndex:(NSInteger)index {
    //cellForIndex的實(shí)現(xiàn)在后面給出,現(xiàn)在只需要知道這是根據(jù)index取到對應(yīng)的cell的方法
    UIView *cell = [self cellForIndex:index];
    
    if (!cell) {
        UIView *cell = [self.dataSource kiraBanner:self viewForItemAtIndex:index % self.numberOfItems];
        cell.clipsToBounds = YES;
        switch (self.bannerType) {
            case KiraBannerTypeHorizontal: {
                float originX = index * self.pageSize.width;
                cell.frame = CGRectMake(originX,
                                        self.topBottomSpace,
                                        self.pageSize.width,
                                        self.pageSize.height);
            }
                break;
            case KiraBannerTypeVertical: {
                float originY = index * self.pageSize.height;
                cell.frame = CGRectMake(self.leftRightSpace,
                                        originY,
                                        self.pageSize.width,
                                        self.pageSize.height);
            }
                break;
            default:
                break;
        }
    }
}
  1. cellForIndex 在上述的fillPageAtIndex方法中,我們看到需要將index對應(yīng)的cell拿出來重用。但是我們的重用池是Set不是Array,無法用下標(biāo)直接訪問,所以需要將index和cell以某種關(guān)系聯(lián)系起來。這邊我們采用的是runtime的關(guān)聯(lián)對象方法,將index轉(zhuǎn)換為NSNumber并將其與cell對象關(guān)聯(lián)在一起。關(guān)聯(lián)的代碼寫在fillPageAtIndex中,然后在cellForIndex方法中讀取,最后在recycleCell的時(shí)候需要進(jìn)行remove操作。
//在fillPageAtIndex中補(bǔ)充
   if (cell) {
            //將cellforindex方法的地址作為objc_setAssociatedObject的key,保證唯一性
            objc_setAssociatedObject(cell, @selector(cellForIndex:),[NSNumber numberWithInteger:index], OBJC_ASSOCIATION_COPY);
            [self.scrollView insertSubview:cell atIndex:0];
        }
- (UIView *) cellForIndex: (NSInteger)index {
    for (UIView *cellView in [self cellSubView]) {
        NSNumber *value = objc_getAssociatedObject(cellView, @selector(cellForIndex:));
        if (value.integerValue == index) {
            return cellView;
        }
    }
    return nil;
}
- (void)recycleCell: (UIView *)cell {
    objc_removeAssociatedObjects(cell);
    [self.reuseCells addObject:cell];
    [cell removeFromSuperview];
}

10.最后在scrollViewDidScroll中調(diào)用setVisibleCellsAtContentOffset方法,理論上到這里是已經(jīng)完成cell的復(fù)用邏輯了。

實(shí)現(xiàn)KiraBanner的循環(huán)輪播

1.在上述過程中已經(jīng)實(shí)現(xiàn)了Cell的復(fù)用邏輯,其實(shí)就是非循環(huán)banner的實(shí)現(xiàn)。但是現(xiàn)在的頁面沒有單頁滑動(dòng)的效果,所以我們需要對ScrollView進(jìn)行設(shè)置。

    self.scrollView.pagingEnabled = YES;

2.reloadData方法 我們需要在reloadData方法中判斷是否循環(huán),如果是,則將scrollView的contentSize設(shè)置為3組banner的大小,并且設(shè)置scrollView的初始contentOffset為第二組banner第一個(gè)的位置。

{
    _needsReload = YES;
    //reload的時(shí)候移除所有cell
    for (UIView *view in self.scrollView.subviews) {
        if ([NSStringFromClass(view.class) isEqualToString:NSStringFromClass(_cellClass.class)]) {
            [view removeFromSuperview];
        }
    }
    //停止計(jì)時(shí)器
    [self stopTimer];
    
    if (_needsReload) {
        if (self.dataSource && [self.dataSource respondsToSelector:@selector(numberOfItemsInKiraBanner:)]) {
            _numberOfItems = [self.dataSource numberOfItemsInKiraBanner:self];
            
            if (self.isCircle) {
                //如果是循環(huán)banner,則把scrollView的長度設(shè)為3組
                _pageCount = self.numberOfItems == 1 ? 1 : self.numberOfItems * 3;
            } else {
                _pageCount = self.numberOfItems == 1 ? 1 : self.numberOfItems;
            }
            
            if (_pageCount == 0) {
                return;
            }
            
            if (self.pageControl && [self.pageControl respondsToSelector:@selector(setNumberOfPages:)]) {
                [self.pageControl setNumberOfPages:self.numberOfItems];
            }
        }
        
        //重置page的寬度
        CGFloat width = _scrollView.bounds.size.width - 4 * self.leftRightSpace;
        
        _pageSize = CGSizeMake(width, width * 9 / 16);
        if (self.delegate && [self.delegate respondsToSelector:@selector(sizeForPageInKiraBanner:)]) {
            _pageSize = [self.delegate sizeForPageInKiraBanner:self];
        }
        
        [_reuseCells removeAllObjects];
        _visibleRange = NSMakeRange(0, 0);
        
        switch (self.bannerType) {
            case KiraBannerTypeHorizontal: {
                [self.scrollView setFrame:CGRectMake(0, 0, _pageSize.width, _pageSize.height)];
                [self.scrollView setContentSize:CGSizeMake(_pageSize.width * _pageCount, 0)];
                _scrollView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
                
                if (self.numberOfItems > 1) {
                    if (self.isCircle) {
//設(shè)置contentOffset為第二組第一個(gè)banner的位置
                        [_scrollView setContentOffset:CGPointMake(_pageSize.width * self.numberOfItems, 0) animated:NO];
                        self.page = self.numberOfItems;
                        [self startTimer];
                    } else {
                        [_scrollView setContentOffset:CGPointMake(0, 0) animated:NO];
                        self.page = self.numberOfItems;
                    }
                }
                
            }
                break;
            case KiraBannerTypeVertical: {
                [self.scrollView setFrame:CGRectMake(0, 0, _pageSize.width, _pageSize.height)];
                [self.scrollView setContentSize:CGSizeMake(0, _pageSize.height * _pageCount)];
                _scrollView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
                
                if (self.numberOfItems > 1) {
                    if (self.isCircle) {
                        [_scrollView setContentOffset:CGPointMake(_pageSize.height * self.numberOfItems, 0) animated:NO];
                        self.page = self.numberOfItems;
                        [self startTimer];
                    } else {
                        [_scrollView setContentOffset:CGPointMake(0, 0) animated:NO];
                        self.page = self.numberOfItems;
                    }
                }
            }
                
                break;
            default:
                break;
        }
        _needsReload = NO;
    }
    
    [self setVisibleCellsAtContentOffset:_scrollView.contentOffset];
//refreshView是做cell在移動(dòng)過程中變化的函數(shù),下面會(huì)給出
    [self refreshView];
}

3.在scrollViewDidScroll方法中對scrollView的contentOffset進(jìn)行判斷。由offset除以cell的寬度計(jì)算出當(dāng)前是第幾個(gè)cell。如果右滑超出第二組最后一個(gè)cell的范圍,就將offset設(shè)置到第二組第一個(gè)cell處,同理如果左滑超出第二組第一個(gè)cell,就將offset設(shè)置到第二組最后一個(gè)cell處,從而達(dá)到循環(huán)的效果。

case KiraBannerTypeHorizontal:
{
     if (scrollView.contentOffset.x / _pageSize.width >= 2 * self.numberOfItems) {
           [scrollView setContentOffset:CGPointMake(_pageSize.width * self.numberOfItems, 0) animated:NO];
           self.page = self.numberOfItems;
     }
                    
     if (scrollView.contentOffset.x / _pageSize.width <= self.numberOfItems - 1) {
           [scrollView setContentOffset:CGPointMake((2 * self.numberOfItems - 1) * _pageSize.width, 0) animated:NO];
           self.page = 2 * self.numberOfItems - 1;
     }
 }
 break;

4.refreshView 在滑動(dòng)過程中根據(jù)offset做transform形變。

- (void)refreshView {
    if (CGRectIsNull(self.scrollView.frame)) {
        return;
    }
    switch (self.bannerType) {
        case KiraBannerTypeHorizontal: {
            CGFloat offset = _scrollView.contentOffset.x;
            for (NSInteger i = self.visibleRange.location; i < self.visibleRange.location + self.visibleRange.length ; i++) {
                UIView *cell = [self cellForIndex:i];
                CGFloat origin = cell.frame.origin.x;
                CGFloat delta = fabs(origin - offset);
                CGRect originCellFrame = CGRectMake(_pageSize.width * i, 0, _pageSize.width, _pageSize.height);
                //TODO:透明度漸變
                
                if (delta < _pageSize.width) {
                    
                    CGFloat leftRightInset = self.leftRightSpace * delta / _pageSize.width;
                    CGFloat topBottomInset = self.topBottomSpace * delta / _pageSize.width;
                    
                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-leftRightInset*2)/_pageSize.width,(_pageSize.height-topBottomInset*2)/_pageSize.height, 1.0);
                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(topBottomInset, leftRightInset, topBottomInset, leftRightInset));
                } else {
                    
                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-self.leftRightSpace * 2)/_pageSize.width,(_pageSize.height-self.topBottomSpace * 2)/_pageSize.height, 1.0);
                    
                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(self.topBottomSpace,
                                                                                         self.leftRightSpace,
                                                                                         self.topBottomSpace,
                                                                                         self.leftRightSpace));
                }
            }
        }
            break;
        case KiraBannerTypeVertical: {
            CGFloat offset = _scrollView.contentOffset.y;
            for (NSInteger i = self.visibleRange.location; i < self.visibleRange.location + self.visibleRange.length ; i++) {
                UIView *cell = [self cellForIndex:i];
                CGFloat origin = cell.frame.origin.y;
                CGFloat delta = fabs(origin - offset);
                CGRect originCellFrame = CGRectMake(0, _pageSize.width * i, _pageSize.width, _pageSize.height);
                //TODO:透明度漸變
                
                if (delta < _pageSize.height) {
                    
                    CGFloat leftRightInset = self.leftRightSpace * delta / _pageSize.height;
                    CGFloat topBottomInset = self.topBottomSpace * delta / _pageSize.height;
                    
                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-leftRightInset*2)/_pageSize.width,(_pageSize.height-topBottomInset*2)/_pageSize.height, 1.0);
                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(topBottomInset, leftRightInset, topBottomInset, leftRightInset));
                } else {
                    
                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-self.leftRightSpace * 2)/_pageSize.width,(_pageSize.height-self.topBottomSpace * 2)/_pageSize.height, 1.0);
                    
                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(self.topBottomSpace,
                                                                                         self.leftRightSpace,
                                                                                         self.topBottomSpace,
                                                                                         self.leftRightSpace));
                }
            }
        }
            break;
            
        default:
            break;
    }
}

5 .自動(dòng)輪播。自動(dòng)輪播就是通過設(shè)置timer來控制自動(dòng)播放。其實(shí)自動(dòng)播放也是通過設(shè)置scrollview的contentOffset來實(shí)現(xiàn)。需要注意在stoptimer的時(shí)候?qū)ime置為nil,并且在合適的時(shí)機(jī)調(diào)用start及stoptimer方法。

- (void)startTimer {
    if (self.numberOfItems > 1 && self.isAutoScroll && self.isCircle) {
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoTime target:self selector:@selector(autoPlay) userInfo:nil repeats:YES];
        self.timer = timer;
        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
}

- (void)stopTimer {
    [self.timer invalidate];
    self.timer = nil;
}

- (void)autoPlay {
    self.page ++;
    switch (self.bannerType) {
        case KiraBannerTypeHorizontal: {
            [_scrollView setContentOffset:CGPointMake(self.page * _pageSize.width, 0) animated:YES];
        }
            break;
        case KiraBannerTypeVertical: {
             [_scrollView setContentOffset:CGPointMake(0, self.page * _pageSize.height) animated:YES];
        }
            break;
        default:
            break;
    }
}

6.Cell的點(diǎn)擊回調(diào)。cell的點(diǎn)擊回調(diào)通過在fillPageAtIndex方法中,對cell添加UITapGestureRecognizer手勢,并且在@selector方法中,通過sender.view來拿到對應(yīng)的cell。然后通過objc_getAssociatedObject拿到該cell關(guān)聯(lián)的index進(jìn)行回調(diào)。

//在fillPageAtIndex函數(shù)中,完善以下代碼
 UIView *cell = [self.dataSource kiraBanner:self viewForItemAtIndex:index % self.numberOfItems];
 UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cellTapped:)];
[cell addGestureRecognizer:tap];
cell.userInteractionEnabled = YES;
- (void)cellTapped:(UITapGestureRecognizer *)sender {
    UIView * cell = sender.view;
    NSInteger index = -1;
    NSNumber *value = objc_getAssociatedObject(cell, @selector(cellForIndex:));
    if (value) {
        index = value.integerValue % self.numberOfItems;
    }
    if ([self.delegate respondsToSelector:@selector(didSelectCell:inKiraBannerAtIndex:)]) {
        [self.delegate didSelectCell:cell inKiraBannerAtIndex:index];
    }
}

END

  • 實(shí)現(xiàn)還有很多不完善的地方,也可能會(huì)存在一些bug,希望大家能及時(shí)指出,一起討論,共同進(jìn)步
  • 這里是gayhub的地址
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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