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,循環滾動。
  • 如何實現無線滾動。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章