在另一篇文章有處理網(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