美文网首页
Objective-C实现循环轮播图

Objective-C实现循环轮播图

作者: zhiziZ | 来源:发表于2021-05-27 17:04 被阅读0次

前几天实现轮播图,调试也费了点时间,顺便做个记录,方便以后使用。

使用pod管理代码,podfile里引入如下库:

pod 'Masonry'
pod 'SDWebImage'

轮播需要定时器,自定义定时器如下,可以防止循环引用,导致内存泄漏:

@interface LZTimer : NSObject

+(void)startWithDuration:(NSInteger)seconds andInterval:(CGFloat)interval intervalOperation:(void(^)(NSInteger))tickBlock finished:(void(^)(void))finishBlock;
+(LZTimer *)timerWithDuration:(NSInteger)seconds andInterval:(CGFloat)interval intervalOperation:(void(^) (NSInteger))tickBlock finished:(void(^)(void))finishBlock;
+(LZTimer *)timerInterval:(CGFloat)interval intervalOperation:(void(^)(void))tickBlock;
-(void)invalidate;
-(void)suspend;
-(void)resume;
@end
@interface LZTimer()

@property (nonatomic, strong)   dispatch_source_t  timer;
@end

@implementation LZTimer

+(void)startWithDuration:(NSInteger)seconds andInterval:(CGFloat)interval intervalOperation:(void(^)(NSInteger))tickBlock finished:(void(^)(void))finishBlock{
    __block NSInteger timeout = seconds/interval;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t _ltimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_ltimer,dispatch_walltime(NULL, 0), interval * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(_ltimer, ^{
        if(timeout <= 0){
            dispatch_async(dispatch_get_main_queue(), ^{
                if(finishBlock) finishBlock();
            });
            dispatch_source_cancel(_ltimer);
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                [UIView beginAnimations:nil context:nil];
                [UIView setAnimationDuration:1];
                if (tickBlock) tickBlock(timeout);
                [UIView commitAnimations];
                timeout--;
            });            
        }
    });
    dispatch_resume(_ltimer);
}

+(LZTimer *)timerWithDuration:(NSInteger)seconds andInterval:(CGFloat)interval intervalOperation:(void(^) (NSInteger))tickBlock finished:(void(^)(void))finishBlock
{
    LZTimer *tm = [[LZTimer alloc] init];
    [tm startWithDuration:seconds andInterval:interval intervalOperation:tickBlock finished:finishBlock];
    return tm;
}

-(void)startWithDuration:(NSInteger)seconds andInterval:(CGFloat)interval intervalOperation:(void(^) (NSInteger))tickBlock finished:(void(^)(void))finishBlock{
    __block NSInteger timeout = seconds/interval;
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer,dispatch_walltime(NULL, 0), interval * NSEC_PER_SEC, 0);
    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.timer, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if(timeout <= 0){
            dispatch_async(dispatch_get_main_queue(), ^{
                if(finishBlock) finishBlock();
            });
            dispatch_source_cancel(strongSelf.timer);
            strongSelf.timer = nil;
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                [UIView beginAnimations:nil context:nil];
                [UIView setAnimationDuration:1];
                if (tickBlock) tickBlock(timeout);
                [UIView commitAnimations];
                timeout--;
            });
        }
    });
    dispatch_resume(self.timer);
}

+(LZTimer *)timerInterval:(CGFloat)interval intervalOperation:(void(^)(void))tickBlock
{
    LZTimer *tm = [[LZTimer alloc] init];
    [tm startWithInterval:interval intervalOperation:tickBlock];
    return tm;
}

-(void)startWithInterval:(CGFloat)interval intervalOperation:(void(^)(void))tickBlock{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer,dispatch_walltime(NULL, 0), interval * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(self.timer, ^{
        if (tickBlock) tickBlock();
    });
    dispatch_resume(self.timer);
}

-(void)invalidate
{
    if (self.timer)
    {
        dispatch_source_cancel(self.timer);
        self.timer = nil;
    }
}

-(void)suspend
{
    if (self.timer)
    {
        dispatch_suspend(self.timer);
    }
}

-(void)resume
{
    if (self.timer)
    {
        dispatch_resume(self.timer);
    }
}
@end

本文的主角轮播图功能代码如下:

@interface LZImageBanner : UIView

@property (nonatomic, copy) NSArray *images_url;
@property (nonatomic, assign) CGFloat bannerWidth;
@end
@interface LZImageBanner()<UIScrollViewDelegate>

@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIPageControl *pageControl;

@property (nonatomic, strong) LZTimer *timer;
@end

@implementation LZImageBanner

-(instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame])
    {
        UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:frame];
        scrollView.delegate = self;
        scrollView.bounces = NO;
        scrollView.pagingEnabled = YES;
        scrollView.showsHorizontalScrollIndicator = NO;
        scrollView.showsVerticalScrollIndicator = NO;
        [self addSubview:scrollView];
        [scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.edges.mas_equalTo(0);
        }];
        self.scrollView = scrollView;
        
        UIPageControl *pageControl = [UIPageControl new];
        pageControl.currentPage = 0;
        pageControl.hidesForSinglePage = YES;
        [self addSubview:pageControl];
        [pageControl mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.equalTo(self);
            make.bottom.mas_equalTo(0);
        }];
        self.pageControl = pageControl;
    }
    return self;
}

-(void)setImages_url:(NSArray *)images_url
{
    _images_url = images_url;
    
    [self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    
    if (images_url.count == 0) return;
    
    if (images_url.count > 1)
    {
        NSMutableArray *cycle_banner_url = [[NSMutableArray alloc] initWithArray:images_url];
        [cycle_banner_url addObject:images_url.firstObject];
        [cycle_banner_url insertObject:images_url.lastObject atIndex:0];
        _images_url = [cycle_banner_url copy];
    }
    
    UIImageView *lastImgView = nil;
    for (NSInteger index = 0; index < _images_url.count; index++)
    {
        UIImageView *imgView = [UIImageView new];
        [self.scrollView addSubview:imgView];
        [imgView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.bottom.mas_equalTo(0);
            if (lastImgView)
            {
                make.left.equalTo(lastImgView.mas_right);
            }
            else {
                make.left.mas_equalTo(0);
            }
            if (index == _images_url.count -1)
            {
                make.right.mas_equalTo(0);
            }
            make.width.equalTo(self.scrollView);
            make.height.equalTo(self.scrollView);
        }];
        [imgView sd_setImageWithURL:[NSURL URLWithString:_images_url[index]] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        }];
        
        lastImgView = imgView;
    }
    self.pageControl.currentPage = 0;
    self.pageControl.numberOfPages = images_url.count;
    if (images_url.count > 1)
    {
        self.scrollView.contentOffset = CGPointMake(self.bannerWidth, 0);
        [self startTimerWithInterval:3.0];
    }
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat currentOffsetX = scrollView.contentOffset.x;
    NSInteger pageIndex = currentOffsetX / self.bannerWidth;

    if (pageIndex >= self.images_url.count - 1)
    {
        self.pageControl.currentPage = 0;
        scrollView.contentOffset = CGPointMake(self.bannerWidth, scrollView.contentOffset.y);
    }
    else if (currentOffsetX <= 0)
    {
        self.pageControl.currentPage = self.pageControl.numberOfPages - 1;
        scrollView.contentOffset = CGPointMake(self.bannerWidth * self.pageControl.numberOfPages, scrollView.contentOffset.y);
    }
    else {
        self.pageControl.currentPage = pageIndex - 1;
    }
}

//-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
//{
//    [self.timer suspend];
//}

//-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
//{
//    [self.timer resume];
//}

-(void)startTimerWithInterval:(NSInteger)interval
{
    [self.timer invalidate];
    __weak typeof(self) ws = self;
    self.timer = [LZTimer timerInterval:interval intervalOperation:^{
        dispatch_async(dispatch_get_main_queue(), ^{
            __strong typeof(ws) ss = ws;
            CGFloat currentOffsetX = ss.scrollView.contentOffset.x;
            NSInteger currentIndex = currentOffsetX / ss.bannerWidth;
            NSInteger targetIndex = currentIndex + 1;

            CGPoint targetOffset = CGPointMake(ss.bannerWidth * targetIndex, ss.scrollView.contentOffset.y);
            
            [ss.scrollView setContentOffset:targetOffset animated:YES];
        });
    }];
}

-(void)dealloc
{
    [self.timer invalidate];
}
@end

轮播图调用代码如下:

LZImageBanner *imgBanner = [LZImageBanner new];
[self.view addSubview:imgBanner];
[imgBanner mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.mas_equalTo(15);
        make.right.mas_equalTo(-15);
        make.height.mas_equalTo(60);
        make.top.mas_equalTo(100);
}];
imgBanner.bannerWidth = [UIScreen mainScreen].bounds.size.width - 30;
imgBanner.images_url = @[path1, path2, path3]; //path1, path2, path3 就是banner图片的url

该轮播图使用中遇到的问题:

  1. dispatch_suspend 状态下直接释放释放定时器,会导致定时器崩溃。
  2. 初始状态(未调用dispatch_resume)直接调用dispatch_source_cancel(timer),app崩溃。
  3. 重复调用dispatch_resume,app崩溃。

相关文章

  • Objective-C实现循环轮播图

    前几天实现轮播图,调试也费了点时间,顺便做个记录,方便以后使用。 使用pod管理代码,podfile里引入如下库:...

  • UIScrollView实现循环轮播Banner(自定义Cell

    实现循环轮播图的各种方案 轮播图的实现方案有很多种,大体上分为CollectionView和ScrollView实...

  • swift第三方控件

    iOS开发:Swift实现的轮播图、无限循环视图控件CYCircularScrollView CycleScrol...

  • js轮播图

    什么是轮播图呢?能够实现多张图片循环出现效果的我们称之为轮播图。 html样式 css样式 js样式 这就是我敲出...

  • UIScrollView 实现循环轮播图

    // // ViewController.m // UCTestAutoScroll // // Created ...

  • OC-循环轮播图

    这里我写了一个文件实现了视觉上是无限循环的轮播图。1、这种轮播图可以有很多种方法实现。在这里是利用UICollec...

  • 轮播图心得

    轮播图 写轮播图之前我们要认识到几个问题:一、什么是轮播图?二、怎么实现轮播效果?三、轮播图还有什么小功能可以实现...

  • 简单轮播图(循环播放)

    效果图 原理 布局 样式 JS实现 以上就是用JS实现的自动循环轮播图,我又加上了鼠标移过停留的效果。

  • Banner

    利用** ViewPager **实现广告 Banner 循环轮播。

  • 任务27 - jquery实战 - 轮播

    代码 实现如下轮播效果 展示、代码 实现上述轮播效果,在一个页面有三个轮播 展示、代码 实现如下无限循环滚动轮播效...

网友评论

      本文标题:Objective-C实现循环轮播图

      本文链接:https://www.haomeiwen.com/subject/yerdsltx.html