iOS-輪播圖(自動(dòng)和循環(huán))本地圖片

在另一篇文章有處理網(wǎng)絡(luò)圖片的方法
可以在GitHub上直接下載文件
https://github.com/peiDuo/PDBannerView
聲明文件部分 只需要調(diào)用initWithFrame方法傳入本地的圖片數(shù)組就可以了

//
//  BannerView.h
//  輪播圖(循環(huán)和自動(dòng))
//
//  Created by 裴鐸 on 2018/3/9.
//  Copyright ? 2018年 裴鐸. All rights reserved.
//

#import <UIKit/UIKit.h>


/**
 7./創(chuàng)建一個(gè)協(xié)議
 需要把文件的名稱傳過(guò)來(lái)
 用@class BannerView;只傳名稱
 */
@class BannerView;

//定義一個(gè)協(xié)議( @protocol 協(xié)議名稱 <NSObject> )
@protocol BannerViewDelegate <NSObject>

/**
 協(xié)議方法實(shí)現(xiàn)方式
 optional 可選 (默認(rèn)是必須實(shí)現(xiàn)的)
 */
@optional

/**
 8./用來(lái)傳值的協(xié)議方法

 @param bannerView 本類
 @param currentImage 傳進(jìn)來(lái)的當(dāng)前的圖片
 */
- (void)selectImage:(BannerView *)bannerView currentImage:(NSInteger)currentImage;

@end//協(xié)議結(jié)束

@interface BannerView : UIView

/**
 9./創(chuàng)建一個(gè)代理屬性(用 weak)
 寫在.h文件里的屬性 外界可以調(diào)用
 */
@property (nonatomic , weak) id <BannerViewDelegate> delegate;

/**
 銷毀定時(shí)器方法
 */
+ (void)destroyTimer;

/**
 1./聲明一個(gè)自定義的構(gòu)造方法 讓外界的對(duì)象用來(lái)初始化bannerView

 @param frame 外界傳入的frame
 @param addImageArray 外界傳入的圖片數(shù)組
 @return 1
 */
- (id)initWithFrame:(CGRect)frame andImageArray:(NSMutableArray *)addImageArray;

@end

實(shí)現(xiàn)文件部分

//
//  BannerView.m
//  輪播圖(循環(huán)和自動(dòng))
//
//  Created by 裴鐸 on 2018/3/9.
//  Copyright ? 2018年 裴鐸. All rights reserved.
//

#import "BannerView.h"

/**
 定時(shí)器 用來(lái)自動(dòng)播放圖片
 */
static NSTimer * mv_timer;

/**
 定義私有變量
 遵守滾動(dòng)式圖的代理方法 實(shí)現(xiàn)拖拽效果
 */
@interface BannerView () <
UIScrollViewDelegate>{
    
    //banNerView的寬和高 私有成員變量用下劃線開頭(書寫習(xí)慣)
    CGFloat mv_width;
    CGFloat mv_height;
}

/**
 分頁(yè)控件
 */
@property (nonatomic , strong) UIPageControl * mainPage;

/**
 scrollView
 */
@property (nonatomic , strong) UIScrollView * mainScrollView;

/**
 圖片數(shù)組
 */
@property (nonatomic , strong) NSMutableArray * dataArray;

@end

@implementation BannerView

//- (instancetype)initWithFrame:(CGRect)frame{
//
//    self = [super initWithFrame:frame];
//
//    if (self) {
//
//       //系統(tǒng)的初始化方法
//    }
//
//    return self;
//}


/**
 2./自定義的init構(gòu)造方法 在.h文件提前聲明

 @param frame 外界初始化時(shí)傳入的frame 帶有bannerView的寬和高
 @param addImageArray 傳入的圖片數(shù)組
 @return 1
 */
- (id)initWithFrame:(CGRect)frame andImageArray:(NSMutableArray *)addImageArray{
    
    //調(diào)用父類方法
    self = [super initWithFrame:frame];
    
    //判斷是否是本類對(duì)象調(diào)用 并 外界傳入的圖片數(shù)量足夠滾動(dòng)
    if (self && addImageArray.count > 2) {
        
        /**
         獲取banNerView 的寬度
         寬和高是外界傳入的 frame (只能在這個(gè)方法內(nèi)有效)
         所以需要定義一個(gè)本類都能使用的成員變量
         */
        mv_width = frame.size.width;
        
        //或取bannerView 的高度
        mv_height = frame.size.height;
        
        //圖片數(shù)組 1 2 3 4 5 6 把外界傳入的圖片數(shù)組賦值給本類的數(shù)組
        self.dataArray = [NSMutableArray arrayWithArray:addImageArray];
        
        //在數(shù)組的最后一位添加傳進(jìn)來(lái)的第一張圖片 1 2 3 4 5 6 1
        [self.dataArray addObject:addImageArray.firstObject];
        
        /**
         在數(shù)組的第一位添加傳進(jìn)來(lái)的最后一張圖片 6 1 2 3 4 5 6 1
         insert 插入元素  atIndex: 根據(jù)下標(biāo)
         */
        [self.dataArray insertObject:addImageArray.lastObject atIndex:0];
        
        //初始化時(shí)把mainscrollView 加載到banNerView上
        [self addSubview:self.mainScrollView];
        
        //初始化時(shí)把分頁(yè)控件加載到bannerView中
        [self addSubview:self.mainPage];
        
        //初始化時(shí)加載定時(shí)器
        [self addTimer];
    }
    
    /**
     返回本類
     當(dāng)外界用本類的初始化方法時(shí)
     返回一個(gè)視圖 bannerView 給外界
     */
    return self;
}

/**
 滾動(dòng)視圖開始手動(dòng)拖拽時(shí)出發(fā)
 */
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    
    //判斷是否有定時(shí)器
    if (mv_timer) {
        
        //如果有定時(shí)器就暫停定時(shí)器(兩種方法實(shí)現(xiàn)暫停效果)
        
        /**
         NSTimer 自帶的方法中沒有暫停和繼續(xù)定時(shí)器的方法
         但是有一個(gè)setFireDate:方法 (定時(shí)器的觸發(fā)時(shí)間)
         原理是把定時(shí)器的觸發(fā)時(shí)間設(shè)置成很久的將來(lái)
         這樣定時(shí)器就會(huì)進(jìn)入等待觸發(fā)的狀態(tài) (實(shí)現(xiàn)暫停效果)
         distantFuture(遙遠(yuǎn)的未來(lái))
         */
        [mv_timer setFireDate:[NSDate distantFuture]];
        
        /**
         用NSTimer自帶的停止定時(shí)器的方法 invalidate
         這個(gè)方法會(huì)吧定時(shí)器永久停止,無(wú)法再次啟用
         所以需要把定時(shí)器清空 nil
         當(dāng)需要再次開啟定時(shí)器時(shí) 重新初始化定時(shí)器
         [self.timer invalidate];
         self.timer = nil;
         */
    }
}

/**
 滾動(dòng)視圖正在滾動(dòng) (拖拽過(guò)程中觸發(fā)的方法)
 */
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
    self.mainPage.currentPage = scrollView.contentOffset.x / mv_width - 1;
}

/**
 滾動(dòng)視圖完成減速時(shí)調(diào)用 (就是手動(dòng)拖拽完成后)
 */
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    
    //判斷是否有定時(shí)器
    if (mv_timer) {
        
        /**
         設(shè)置定時(shí)器的觸發(fā)時(shí)間
         延后2秒觸發(fā)
         */
        [mv_timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
        
        /**
         重新初始化定時(shí)器
         self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerFUNC:) userInfo:nil repeats:YES];
         */
    }
    
    //獲取當(dāng)前滾動(dòng)視圖的偏移量
    CGPoint currentPoint = scrollView.contentOffset;
    
    /** 判斷拖拽完成后將要顯示的圖片時(shí)第幾張 6 1 2 3 4 5 6 1 */
    //如果是數(shù)組內(nèi)的最后一張圖片 1
    if (currentPoint.x == (self.dataArray.count - 1) * mv_width) {
        
        //改變偏移量 顯示數(shù)組內(nèi)的第一張圖片 1
        scrollView.contentOffset = CGPointMake(mv_width, 0);
    }
    
    //如果是數(shù)組內(nèi)的第一張圖片 6
    if (currentPoint.x == 0) {
        
        //改變偏移量 顯示數(shù)組內(nèi)的 第二個(gè)圖片6
        scrollView.contentOffset = CGPointMake((self.dataArray.count - 2) * mv_width, 0);
    }
    
    /**
     如果是圖片數(shù)組的第一張圖片 或 最后一張圖片時(shí)
     滾動(dòng)視圖的偏移量發(fā)生了改變
     所以之前的偏移量變量不能再使用了 (獲取一個(gè)新的偏移量)
     */
    //獲取新的滾佛那個(gè)視圖偏移量
    CGPoint newPoint = scrollView.contentOffset;
    
    //改變分頁(yè)控件上的頁(yè)碼
    self.mainPage.currentPage = newPoint.x / mv_width - 1;
}

/**
 5./初始化定時(shí)器
 */
- (void)addTimer{
    
    //初始化定時(shí)器 時(shí)間戳:2.0秒 目標(biāo):本類 方法選擇器:timerFUNC 用戶信息:nil 是否循環(huán):yes
    mv_timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerFUNC:) userInfo:nil repeats:YES];
    
    /**
     將定時(shí)器添加到當(dāng)前線程中(currentRunLoop 當(dāng)前線程)
     [NSRunLoop currentRunLoop]可以的到一個(gè)當(dāng)前線程下的NSRunLoop對(duì)象
     addTimer:添加一個(gè)定時(shí)器
     forMode:什么模式
     NSRunLoopCommonModes 共同模式
     */
    [[NSRunLoop currentRunLoop] addTimer:mv_timer forMode:NSRunLoopCommonModes];
    /**
     在開啟一個(gè)NSTimer實(shí)質(zhì)上是在當(dāng)前的runloop中注冊(cè)了一個(gè)新的事件源,
     而當(dāng)scrollView滾動(dòng)的時(shí)候,當(dāng)前的MainRunLoop是處于UITrackingRunLoopMode的模式下,
     在這個(gè)模式下,是不會(huì)處理NSDefaultRunLoopMode的消息(因?yàn)镽unLoop 的 Mode不一樣),
     要想在scrollView滾動(dòng)的同時(shí)也接受其它runloop的消息,我們需要改變兩者之間的runLoopMode.
     簡(jiǎn)單的說(shuō)就是NSTimer不會(huì)開啟新的進(jìn)程,只是在RunLoop里注冊(cè)了一下,
     RunLoop每次loop時(shí)都會(huì)檢測(cè)這個(gè)timer,看是否可以觸發(fā)。
     當(dāng)Runloop在A mode,而timer注冊(cè)在B mode時(shí)就無(wú)法去檢測(cè)這個(gè)timer,
     所以需要把NSTimer也注冊(cè)到A mode,這樣就可以被檢測(cè)到。
     所以模式參數(shù) forMode: 填寫 NSRunLoopCommonModes 共同模式
     */
}

/**
 6./實(shí)現(xiàn)定時(shí)器方法
 */
- (void)timerFUNC:(NSTimer *)timer{
    
    /**
     獲取當(dāng)前圖片的X位置
     也就是定時(shí)器再次出發(fā)時(shí)滾動(dòng)視圖上正在顯示的是哪一張圖片
     */
    CGFloat currentX = self.mainScrollView.contentOffset.x;
    
    /**
     獲取下一張圖片的X位置
     當(dāng)前位置 + 一個(gè)屏幕寬度
     */
    CGFloat nextX = currentX + mv_width;
    
    /**
     判斷滾動(dòng)視圖上將要顯示的圖片是最后一張時(shí)
     通過(guò)X值來(lái)判斷 所以要 self.dataArray.count - 1
     */
    if (nextX == (self.dataArray.count - 1) * mv_width) {
        
        /**
         UIView的動(dòng)畫效果方法(分兩個(gè)方法)
         */
        [UIView animateWithDuration:0.2 animations:^{
            /**
             動(dòng)畫效果的第一個(gè)方法
             Duration:持續(xù)時(shí)間
             animations:動(dòng)畫內(nèi)容
             這個(gè)動(dòng)畫執(zhí)行 0.2秒 后進(jìn)入下一個(gè)方法
             */
            
            //往最后一張圖片走
            self.mainScrollView.contentOffset = CGPointMake(nextX, 0);
            
            /**
             改變對(duì)應(yīng)的分頁(yè)控件顯示圓點(diǎn)
             */
            self.mainPage.currentPage = 0;
        } completion:^(BOOL finished) {
            /**
             動(dòng)畫效果的第二個(gè)方法
             completion: 回調(diào)方法 (完成\結(jié)束的意思)
             上一個(gè)方法結(jié)束后進(jìn)入這個(gè)方法
             */
            
            //往第二張圖片走
            self.mainScrollView.contentOffset = CGPointMake(self->mv_width, 0);
        }];
    }else{//如果滾動(dòng)視圖上要顯示的圖片不是最后一張時(shí)
        
        //顯示下一張圖片
        [UIView animateWithDuration:0.2 animations:^{
            
            //讓下一個(gè)圖片顯示出來(lái)
            self.mainScrollView.contentOffset = CGPointMake( nextX, 0);
            
            //改變對(duì)應(yīng)的分頁(yè)控件顯示圓點(diǎn)
            self.mainPage.currentPage = self.mainScrollView.contentOffset.x / self->mv_width - 1;
        } completion:^(BOOL finished) {
            
            //改變對(duì)應(yīng)的分頁(yè)控件顯示圓點(diǎn)
            self.mainPage.currentPage = self.mainScrollView.contentOffset.x / self->mv_width - 1;
        }];
    }
}

/**
 4./加載分頁(yè)控件
 */
- (UIPageControl *)mainPage{
    
    if (!_mainPage) {
        
        //初始化分頁(yè)控制器
        _mainPage = [[UIPageControl alloc]initWithFrame:CGRectMake( 50, mv_height - 20, mv_width - 50 * 2, 20)];
        
        //分頁(yè)控件上要顯示的圓點(diǎn)數(shù)量
        _mainPage.numberOfPages = self.dataArray.count - 2;
        //分頁(yè)控件不允許和用戶交互(不許點(diǎn)擊)
        _mainPage.userInteractionEnabled = NO;
        
        //設(shè)置 默認(rèn)點(diǎn) 的顏色
        _mainPage.pageIndicatorTintColor = [UIColor whiteColor];
        
        //設(shè)置 滑動(dòng)點(diǎn)(當(dāng)前點(diǎn)) 的顏色
        _mainPage.currentPageIndicatorTintColor = [UIColor greenColor ];
    }
    
    return _mainPage;
}

/**
 3./加載滾動(dòng)視圖
 */
- (UIScrollView *)mainScrollView{
    
    if (!_mainScrollView) {
        
        //初始化滾動(dòng)控件
        _mainScrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 0, mv_width, mv_height)];
        
        //滾動(dòng)式圖的代理
        _mainScrollView.delegate = self;
        
        /**
         滾動(dòng)范圍(手動(dòng)拖拽時(shí)的范圍)
         如果不寫就不能手動(dòng)拖拽(但是定時(shí)器可以讓圖片滾動(dòng))
         */
        _mainScrollView.contentSize = CGSizeMake(self.dataArray.count * mv_width, mv_height);
        
        //分頁(yè)滾動(dòng)效果 yes
        _mainScrollView.pagingEnabled = YES;
        
        //能否滾動(dòng)
        _mainScrollView.scrollEnabled = YES;
        
        //彈簧效果 NO
        _mainScrollView.bounces = NO;
        
        //滾動(dòng)視圖的起始偏移量
        _mainScrollView.contentOffset = CGPointMake(mv_width, 0);
        
        //垂直滾動(dòng)條
        _mainScrollView.showsVerticalScrollIndicator = NO;
        
        //水平滾動(dòng)條
        _mainScrollView.showsHorizontalScrollIndicator = NO;
        
        /**
         循環(huán)往滾動(dòng)視圖上添加圖片視圖
         循環(huán)條件 i < self.dataArray.count 一定不要寫等號(hào) =
         如果 i <= self.dataArray.count 程序就會(huì)崩潰,(下標(biāo)越界)
         */
        for (int i = 0; i < self.dataArray.count; i ++) {
            
            //初始化圖片視圖
            UIImageView * imgV = [[UIImageView alloc]initWithFrame:CGRectMake(mv_width * i, 0, mv_width, mv_height)];
            
            //給圖片視圖添加圖片 通過(guò)圖片數(shù)組
            imgV.image = [UIImage imageNamed:self.dataArray[i]];
            
            //讓圖片可以與用戶交互
            imgV.userInteractionEnabled = YES;
            
            //初始化一個(gè)點(diǎn)擊手勢(shì)
            UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAcyion:)];
            
            //把點(diǎn)擊手勢(shì)添加到圖片上
            [imgV addGestureRecognizer:tap];
            
            //把圖片視圖 添加 到滾動(dòng)視圖上
            [_mainScrollView addSubview:imgV];
        }
    }
    
    //返回滾動(dòng)視圖 給 bannerView
    return _mainScrollView;
}
/**
 點(diǎn)擊圖片觸發(fā)的手勢(shì)方法
 */
- (void)tapAcyion:(UITapGestureRecognizer *)tap{
    
    /**
     如果代理屬性能夠響應(yīng)協(xié)議方法方法
     才會(huì)通過(guò)代理屬性 調(diào)用協(xié)議方法
     */
    if ([self.delegate respondsToSelector:@selector(selectImage:currentImage:)]) {
        
        /**
         通過(guò)代理屬性 調(diào)用協(xié)議方法
         currentImage:當(dāng)前的圖片時(shí)第幾張
         可以通過(guò)分頁(yè)控件的當(dāng)前圓點(diǎn)來(lái)判斷是第幾張圖片
         */
        [self.delegate selectImage:self currentImage:self.mainPage.currentPage];
    }
}

+ (void)destroyTimer{
    //  清理定時(shí)器
    [mv_timer invalidate];
    mv_timer = nil;
}


@end

最后編輯于
?著作權(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)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,262評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,672評(píng)論 1 32
  • 1、通過(guò)CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫(kù)組件 SD...
    陽(yáng)明AI閱讀 16,211評(píng)論 3 119
  • QQ第三方登錄不同平臺(tái)(IOS,Android,網(wǎng)站)如何識(shí)別同一用戶?https://segmentfault....
    itlover2013閱讀 156評(píng)論 0 1
  • 立枯病又稱“死苗立枯病多在育苗中后期發(fā)生,發(fā)病中無(wú)絮狀白霉、植株得病過(guò)程中不倒伏。 發(fā)病規(guī)律 病菌以菌絲和菌核在土...
    萬(wàn)能沖閱讀 963評(píng)論 0 0

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