UIScrollView和UIPageControl的分页 && NSTimer【图片轮播器】

UIScrollView和UIPageControl的分页

一、分页

  • 只要将UIScrollView的pageEnabled属性设置为YES,UIScrollView会被分割成多个独立页面,里面的内容就能进行分页展示。(根据ScrollView的规格进行分页)
  • 一般会配合UIPageControl增强分页效果。

二、UIPageControl常用属性

  • 一共有多少页
    @property(nonatomic) NSInteger numberOfPages;
  • 当前显示的页码
    @property(nonatomic) NSInteger currentPage;
  • 只有一页时,是否需要隐藏页码指示器
    @property(nonatomic) BOOL hidesForSinglePage;
  • 其他页码指示器的颜色
    @property(nonatomic,retain) UIColor *pageIndicatorTintColor;
  • 当前页码指示器的颜色
    @property(nonatomic,retain) UIColor *currentPageIndicatorTintColor;

后两个属性在storyboard上设置更方便。


NSTimer“定时器”

作⽤

  • 在指定的时间执⾏行指定的任务
  • 每隔⼀段时间执⾏行指定的任务

常用方法

  1. 调⽤下⾯的方法就会开启一个定时任务,而且定时器会自己启动(自己走)。
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
                               target:(id)aTarget
                               selector:(SEL)aSelector
                               userInfo:(id)userInfo
                               repeats:(BOOL)yesOrNo;

这个方法的意思为:每隔 ti 秒,调⽤一次 aTarget 的 aSelector 方法 , yesOrNo 决定了是否重复执⾏这个任务。
2. 还有一个方法也可以设置定时器,只不过这个方法返回的定时器是不会自己走的,需要调用-(void)fire;方法,才可以让定时器开始走。
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
3. 停⽌定时器 (一旦定时器被停止了,就不能再次执行任务。只能再创建一个新的定时器才能执⾏新的任务)
- (void)invalidate;

注意

  • 通过invalidate方法可以停⽌止定时器的工作,一旦定时器被停止了,就不能再次执行任 务。只能再创建一个新的定时器才能执行新的任务。
  • 定时器还有 CADisplayLink 类,这个类一般用于做游戏,时间间隔比较大的话一般用 NSTimer 类,时间间隔非常小的话一般用 CADisplayLink 类。

图片轮播器

结构

这里写图片描述

代码

#define ImageCount 5

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
/**
 *  定时器
 */
@property (nonatomic, strong) NSTimer *timer;
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 0.一些固定的尺寸参数
    CGFloat imageW = self.scrollView.frame.size.width;
    CGFloat imageH = self.scrollView.frame.size.height;
    CGFloat imageY = 0;

    // 1.添加5张图片到scrollView中
    for (int i = 0; i<ImageCount; i++) {
        UIImageView *imageView = [[UIImageView alloc] init];

        // 设置frame
        CGFloat imageX = i * imageW;
        imageView.frame = CGRectMake(imageX, imageY, imageW, imageH);

        // 设置图片
        NSString *name = [NSString stringWithFormat:@"img_0%d", i + 1];
        imageView.image = [UIImage imageNamed:name];

        [self.scrollView addSubview:imageView];

        // [self.scrollView insertSubview:imageView atIndex:0];
        // 这个方式是将子控件插入到特定位置
    }

    // 2.设置内容尺寸
    CGFloat contentW = ImageCount * imageW;
    self.scrollView.contentSize = CGSizeMake(contentW, 0);

    // 3.隐藏水平滚动条
    self.scrollView.showsHorizontalScrollIndicator = NO;

    // 4.分页
    self.scrollView.pagingEnabled = YES;
    // 这里设置控制器为UIScrollView的delegate是在storyboard中托线实现的  
    // self.scrollView.delegate = self;

    // 5.设置pageControl的总页数
    self.pageControl.numberOfPages = MJImageCount;

    // 6.添加定时器(每隔2秒调用一次self 的nextImage方法)
    [self addTimer];
}

/**
 *  添加定时器
 */
- (void)addTimer
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

/**
 *  移除定时器
 */
- (void)removeTimer
{
    [self.timer invalidate];
    self.timer = nil;
}

- (void)nextImage
{
    // 1.增加pageControl的页码
    int page = 0;
    if (self.pageControl.currentPage == ImageCount - 1) {
        page = 0;
    } else {
        page = self.pageControl.currentPage + 1;
    }

    // 2.计算scrollView滚动的位置
    CGFloat offsetX = page * self.scrollView.frame.size.width;
    CGPoint offset = CGPointMake(offsetX, 0);
    [self.scrollView setContentOffset:offset animated:YES];
}

#pragma mark - 代理方法
/**
 *  当scrollView正在滚动就会调用
 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // 根据scrollView的滚动位置决定pageControl显示第几页
    CGFloat scrollW = scrollView.frame.size.width;
    int page = (scrollView.contentOffset.x + scrollW * 0.5) / scrollW;
    self.pageControl.currentPage = page;
}

/**
 *  开始拖拽的时候调用
 */
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    // 停止定时器(一旦定时器停止了,就不能再使用)
    [self removeTimer];
}

/**
 *  停止拖拽的时候调用
 */
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    // 开启定时器
    [self addTimer];
}
@end

遇见的问题

  • 当定时器的时间累积到间隔 ti 时间后,定时器会执行 aSelector 方法,但用户拖拽ScrollView时,定时器还在计时,由于用户的拖拽,当时定时器没法执行 aSelector 方法,但会累积方法的次数,等用户停止拖拽,定时器会把累积的方法都执行。这样就达不到我们要的效果。解决方法如下:
    • 当用户拖拽ScrollView时,移除(停止)计时器。(计时器一但停止,就不能再使用了,为了节省内存,把计时器设置为 nil )
    • 当用户停止拖拽ScrollView时,新创建一个计时器。
  • 线程问题
    • 一个程序默认会开一条线程,默认开的线程我们称之为主线程,主线程会做两件事情:(1)处理UI,刷新UI;(2)处理用户的触摸事件,滚动事件。
    • 凡是跟UI有关的事情都是主线程在处理,一条线程同一时间只能处理一件事情。
    • 假如有另一个触摸事件正在进行,那么图片轮播器就不会执行,这时想到可以用多线程来处理这两个事件,但是这种想法是错的,UI界面的刷新只能交给主线程处理。再开另一个线程处理图片轮播器是不可以的,所以还是要用一条线程处理两个事情。正确的解决方法是分流,提高图片轮播器的优先级,线程在处理其他事情的时候,留出来一些时间处理图片轮播器。
    • [[NSRunLoop currentRunLoop] addTimer:<#(NSTimer *)#> forMode:<#(NSString *)#>] forMode:有两种:默认是NSDefaultRunLoopMode 没有优先级;NSRunLoopCommonModes 有优先级。
  • 这个图片浏览器只有5个图片,一张图片对应一个ImageView,但假如有10000张图片。按照上面的方法,我们会创建10000个ImageView,这样太浪费内存了。有两种改进方法:(1)用到的时候再创建; (2)循环利用ImageView,循环滚动。
  • 如何实现无线滚动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值