之前利用過(guò)零零散散的時(shí)間封裝了一個(gè)屬于自己圖片瀏覽器框架,代碼量總共也就五六百行吧,昨天剛剛封裝完成闷串,今天想拿來(lái)分享總結(jié)一下祖搓。寫這篇文章的最主要目的不是想宣傳自己的框架狱意,重點(diǎn)是想整理一下思路,并且告訴看了此文章的人拯欧,如何也能封裝一個(gè)自己的圖片瀏覽器框架详囤。自我感覺(jué)封裝的還算良好,支持長(zhǎng)圖顯示、捏合手勢(shì)藏姐、單雙擊手勢(shì)隆箩、支持動(dòng)畫效果、支持長(zhǎng)按保存圖片羔杨。后期再給補(bǔ)充一個(gè)顯示gif圖片的動(dòng)能捌臊,之后再抽空發(fā)布到pods上。效果動(dòng)態(tài)圖如下:
代碼量沒(méi)有多少兜材,一旦學(xué)會(huì)了封裝自己的框架理澎,以后網(wǎng)上的一些第三方完全可以不用,并且還能根據(jù)項(xiàng)目需求改變不同的樣式曙寡,想想都挺激動(dòng)地糠爬。??????如果肯結(jié)合這篇文章花上半天到一天的時(shí)間去研究一下,基本上也能封裝一套自己的圖片瀏覽器框架举庶。
看這篇文章一定要結(jié)合源碼看执隧,因?yàn)闉榱丝刂莆恼缕抑徽f(shuō)明了封裝過(guò)程的總體思路和中間的一些注意點(diǎn)户侥。源碼下載地址:https://github.com/ZhengYaWei1992/ZWPhotoBrowser
還是老樣子殴玛,先看一下如何使用,對(duì)外提供的接口是什么樣添祸。說(shuō)明:下面的第二個(gè)代理方法是必須要實(shí)現(xiàn)的滚粟,主要原因是在動(dòng)畫效果中的時(shí)候,要獲取到上一界面的原本圖片刃泌,用這個(gè)圖片做動(dòng)畫效果凡壤。當(dāng)imageView的容器視圖為一般的UIView的時(shí)候沒(méi)有問(wèn)題。但是當(dāng)容器視圖為UICollectionView的時(shí)候耙替,通過(guò)sourceImagesContainerView還要做額外判斷亚侠,情況不定。所以通過(guò)代理更為合理俗扇!這里考慮還是比較全面的硝烂,考慮到容器視圖為collectionView情況。
- (void)ivTap:(UITapGestureRecognizer *)tap{
ZWPhotoBrowser *photoBrowser = [[ZWPhotoBrowser alloc]init];
photoBrowser.delegate = self;
photoBrowser.currentImageIndex = tap.view.tag;
photoBrowser.imageCount = self.smallPicsUrls.count;
photoBrowser.sourceImagesContainerView = _containerView;
photoBrowser.placeholderImage = [UIImage imageNamed:@"placeholder"];
[photoBrowser show];
}
#pragma mark -ZWPhotoBrowserDelegate
/**
返回高清圖像的URL铜幽,如果沒(méi)有寫這個(gè)方法滞谢,則顯示小圖像
*/
- (NSURL *)photoBrowser:(ZWPhotoBrowser *)browser highQualityImageURLForIndex:(NSInteger)index{
return [NSURL URLWithString:self.bigPicsUrls[index]];
}
/**
獲取imageView上的原本小圖 這是必須實(shí)現(xiàn)的代理方法
主要是為了設(shè)置動(dòng)畫效果
*/
- (UIImage *)photoBrowser:(ZWPhotoBrowser *)browser smallImageForIndex:(NSInteger)index{
UIImageView *imageView = self.containerView.subviews[index];
return imageView.image;
}
先在這里補(bǔ)充一個(gè)小知識(shí)點(diǎn),上面效果圖的大圖加載動(dòng)畫是自己寫的除抛,主要通過(guò)繪圖實(shí)現(xiàn)的狮杨,demo中有專門封裝的一個(gè)類。之前也寫過(guò)一篇文章關(guān)于進(jìn)度加載的控件的實(shí)現(xiàn)到忽,可以參考我之前寫的這篇文章:http://www.reibang.com/p/580298595e10
說(shuō)一下圖片瀏覽器的層次結(jié)構(gòu)橄教,實(shí)際上是有兩層UIScrollView,首先每個(gè)imageView都有一個(gè)一一對(duì)應(yīng)的UIScrollView父視圖。然后多張圖片的連續(xù)現(xiàn)實(shí)也是借助UIScrollView實(shí)現(xiàn)的护蝶。另外圖片瀏覽器一般是放置在UIWindow上顯示的华烟,這樣管理起來(lái)更科學(xué),實(shí)現(xiàn)起來(lái)動(dòng)畫效果也比較方便持灰。具體實(shí)現(xiàn)代碼可以看 [photoBrowser show];這個(gè)方法垦江。
看看我在封裝這個(gè)框架中一步一步是怎么做的:
1、首先是封裝了一個(gè)加載動(dòng)畫的ZWWaitingView
2搅方、之后封裝了一個(gè)繼承于UIImageView的單張圖片顯示的類ZWBrowserImageView比吭,這個(gè)類主要是負(fù)責(zé)單張圖片的顯示,并且添加有捏合手勢(shì)控制圖片大幸涛小衩藤;上面效果圖中單張圖片測(cè)試按鈕點(diǎn)擊進(jìn)入,就是對(duì)應(yīng)這一步的成果涛漂。
3赏表、最后封裝了一個(gè)繼承于UIView的ZWPhotoBrowser,然后創(chuàng)建一個(gè)scrollView對(duì)象匈仗,將對(duì)應(yīng)的ZWBrowserImageView實(shí)例對(duì)象依次放置到scrollView上瓢剿,動(dòng)畫效果、單雙擊手勢(shì)悠轩、保存圖片间狂、圖片張數(shù)顯示等實(shí)現(xiàn)邏輯都放置在這個(gè)類中。
==================第一步驟========================
上面的第一步就不多說(shuō)了火架,具體請(qǐng)看我之前寫的文章鉴象。http://www.reibang.com/p/580298595e10
==================第二步驟========================
看看第二步的實(shí)現(xiàn)吧。實(shí)現(xiàn)單張圖片的顯示的外部接口調(diào)用代碼何鸡。
ZWBrowserImageView *iv = [[ZWBrowserImageView alloc]initWithFrame:CGRectMake(0, 80, self.view.frame.size.width, self.view.frame.size.height - 80 - 49)];
//設(shè)置圖片內(nèi)容
[iv setImageWithURL:[NSURL URLWithString:@"http://weixintest.ihk.cn/ihkwx_upload/commentPic/20160519/14636417292422.jpg"] placeholderImage:[UIImage imageNamed:@"placholder"]];
[self.view addSubview:iv];
ZWBrowserImageView這個(gè)類中纺弊,針對(duì)于縮放狀態(tài)和非縮放狀態(tài)分別創(chuàng)建了scroll、scrollImageView和zoomingScrollView骡男、zoomingImageView淆游。這樣管理起來(lái)更方便,實(shí)現(xiàn)顯示的時(shí)候通過(guò)視圖層次結(jié)構(gòu)的遮擋隔盛,視覺(jué)上形成具體效果就好犹菱。另外只有借助scrollView才能更好的實(shí)現(xiàn)捏合手勢(shì)。
設(shè)置imageView圖片的實(shí)現(xiàn)代碼骚亿,包含進(jìn)度加載視圖的邏輯代碼已亥。
#pragma mark - 設(shè)置imageView內(nèi)容
- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder{
//1.添加加載視圖
ZWWaitingView *waitingView = [[ZWWaitingView alloc]init];
waitingView.bounds = CGRectMake(0, 0, 80, 80);
waitingView.clipsToBounds = YES;
waitingView.layer.cornerRadius = 40;
//默認(rèn)餅形加載視圖
waitingView.mode = ZWWaitingViewModePieDiagram;
_waitingView = waitingView;
[self addSubview:waitingView];
// __weak typeof(self) imageViewWeak = self;
__weak ZWBrowserImageView *imageViewWeak = self;
[self sd_setImageWithURL:url placeholderImage:placeholder options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {
//設(shè)置進(jìn)度,調(diào)用setProgress方法
imageViewWeak.progress = (CGFloat)receivedSize / expectedSize;
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
//加載完成移除waitingView
[_waitingView removeFromSuperview];
if(error){//圖片加載失敗
UILabel *label = [[UILabel alloc] init];
label.bounds = CGRectMake(0, 0, 160, 30);
label.center = CGPointMake(imageViewWeak.bounds.size.width * 0.5, imageViewWeak.bounds.size.height * 0.5);
label.text = @"圖片加載失敗";
label.font = [UIFont systemFontOfSize:16];
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.8];
label.layer.cornerRadius = 5;
label.clipsToBounds = YES;
label.textAlignment = NSTextAlignmentCenter;
[imageViewWeak addSubview:label];
}else{//圖片加載成功
_scrollImageView.image = image;
//加載成功,調(diào)用layoutSubviews方法
[_scrollImageView setNeedsDisplay];
}
}];
}
單張圖片捏合手勢(shì)實(shí)現(xiàn)邏輯来屠。
#pragma mark -手勢(shì)捏合事件
- (void)zoomImage:(UIPinchGestureRecognizer *)recognizer{
[self prepareForImageViewScaling];
//設(shè)置縮放比例
CGFloat scale = recognizer.scale;
CGFloat temp = _totalScale + (scale - 1);
[self setTotalScale:temp];
recognizer.scale = 1.0;
}
//設(shè)置縮放比例
- (void)setTotalScale:(CGFloat)totalScale{
//最大縮放 2倍,最小0.5倍
if ((_totalScale < 0.5 && totalScale < _totalScale) || (_totalScale > 2.0 && totalScale > _totalScale)){
return;
}
[self zoomWithScale:totalScale];
}
- (void)zoomWithScale:(CGFloat)scale{
_totalScale = scale;
_zoomingImageView.transform = CGAffineTransformMakeScale(scale, scale);
if (scale > 1) {//放大
CGFloat contentW = _zoomingImageView.frame.size.width;
//??俱笛?捆姜??
CGFloat contentH = MAX(_zoomingImageView.frame.size.height, self.frame.size.height);
_zoomingImageView.center = CGPointMake(contentW * 0.5, contentH * 0.5);
_zoomingScrollView.contentSize = CGSizeMake(contentW, contentH);
CGPoint offset = _zoomingScrollView.contentOffset;
offset.x = (contentW - _zoomingScrollView.frame.size.width) * 0.5;
//開(kāi)啟了這句話迎膜,放大啊圖片的時(shí)候會(huì)產(chǎn)生錯(cuò)位泥技,體驗(yàn)效果不是很好
//offset.y = (contentH - _zoomingImageView.frame.size.height) * 0.5;
_zoomingScrollView.contentOffset = offset;
}else{//縮小
_zoomingScrollView.contentSize = _zoomingScrollView.frame.size;
//縮小時(shí),同時(shí)設(shè)置內(nèi)容填充邊距為0
_zoomingScrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
_zoomingImageView.center = _zoomingScrollView.center;
}
}
雙擊手勢(shì)事件實(shí)現(xiàn)邏輯代碼磕仅。
#pragma mark - 對(duì)外提供的方法 public
//雙擊點(diǎn)擊事件
- (void)doubleTapToZommWithScale:(CGFloat)scale{
[self prepareForImageViewScaling];
[UIView animateWithDuration:0.5 animations:^{
[self zoomWithScale:scale];
} completion:^(BOOL finished) {
//動(dòng)畫完成珊豹,如果scale為1,則移除zoomingScrollView和zoomingImageView
if (scale == 1) {
[self clear];
}
}];
}
上面代碼只是第二步驟中的部分重點(diǎn)代碼榕订,借助scrollView可以實(shí)現(xiàn)長(zhǎng)圖的展示店茶,雙擊放大縮小事件,捏合手勢(shì)事件劫恒。這樣一張圖片就可以簡(jiǎn)單的展示出來(lái)贩幻。
==================第三步驟========================
主要借助scrollView合理的展示第二步中ZWBrowserImageView實(shí)例對(duì)象。在這一步中主要封裝了ZWPhotoBrowser這個(gè)類两嘴,這個(gè)類中的幾個(gè)注意點(diǎn)丛楚。
a、進(jìn)入圖片瀏覽器動(dòng)畫顯示憔辫。
b趣些、scrollView滑動(dòng)到一定位置的時(shí)候再去加載圖片,而不是一次全部將圖片都加載完贰您,否則當(dāng)圖片過(guò)多的時(shí)候喧务,一次加載所有資源圖片消耗內(nèi)存非常大,嚴(yán)重時(shí)直接導(dǎo)致卡死枉圃。
c功茴、在滾動(dòng)到下一張圖片的股喲城中,每張圖片之間是有一定的間隙孽亲,這個(gè)要通過(guò)合理的布局才能顯示巍实。
d、在UIWindow上展示
***************a敢朱、動(dòng)畫效果*******************
代碼中含有注釋窟蓝。
/**
展示的第一張圖片要做特殊的處理
*/
- (void)showFirstImage{
UIView *sourceView = nil;
if ([self.sourceImagesContainerView isKindOfClass:UICollectionView.class]) {//容器視圖為UICollectionView
UICollectionView *view = (UICollectionView *)self.sourceImagesContainerView;
NSIndexPath *path = [NSIndexPath indexPathForItem:self.currentImageIndex inSection:0];
sourceView = [view cellForItemAtIndexPath:path];
}else{//容器視圖為一般的UIView
sourceView = self.sourceImagesContainerView.subviews[self.currentImageIndex];
}
CGRect rect = [self.sourceImagesContainerView convertRect:sourceView.frame toView:self];
UIImageView *tempView = [[UIImageView alloc] init];
tempView.image = [self smallImageForIndex:self.currentImageIndex];
[self addSubview:tempView];
CGRect targetTemp = [_scrollView.subviews[self.currentImageIndex] bounds];
tempView.frame = rect;
tempView.contentMode = [_scrollView.subviews[self.currentImageIndex] contentMode];
//執(zhí)行動(dòng)畫之前先隱藏scrollView,動(dòng)畫執(zhí)行完成后顯示scrollView
_scrollView.hidden = YES;
[UIView animateWithDuration:ZWPhotoBrowserShowImageAnimationDuration animations:^{
tempView.center = self.center;
tempView.bounds = (CGRect){CGPointZero, targetTemp.size};
} completion:^(BOOL finished) {
_hasShowedFirstView = YES;
[tempView removeFromSuperview];
_scrollView.hidden = NO;
}];
}
***************b篮绿、滾動(dòng)過(guò)程中加載資源*******************
#pragma mark - scrollview代理方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
int index = (scrollView.contentOffset.x + _scrollView.bounds.size.width * 0.5) / _scrollView.bounds.size.width;
// 有過(guò)縮放的圖片在拖動(dòng)一定距離后清除縮放
CGFloat margin = 150;
CGFloat x = scrollView.contentOffset.x;
if (x - index *self.bounds.size.width > margin || x - index * self.bounds.size.width < -margin) {
ZWBrowserImageView *imageView = _scrollView.subviews[index];
if (imageView.isScaled) {
[UIView animateWithDuration:0.5 animations:^{
imageView.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
[imageView eliminateScale];
}];
}
}
//設(shè)置UILabel顯示內(nèi)容
if (!_willDisappear) {
_indexLabel.text = [NSString stringWithFormat:@"%d/%ld", index + 1, (long)self.imageCount];
}
//滑動(dòng)的時(shí)候加載下一張圖片
[self setupImageOfImageViewForIndex:index];
}
***************c孵延、合理的布局,顯示分割線*******************
請(qǐng)看代碼中的注釋
/**
layoutSubviews方法
注意:為了讓圖片在滾動(dòng)的時(shí)候顯示間距的特殊布局做法亲配。
scrollView寬度左右兩邊各加10 尘应, scrollView的子視圖左右兩邊也各加10惶凝,contentSize依然同常規(guī)的一樣,然后scrollView按頁(yè)滾動(dòng)便實(shí)現(xiàn)中間有分割線的效果
*/
- (void)layoutSubviews{
[super layoutSubviews];
CGRect rect = self.bounds;
rect.size.width += ZWPhotoBrowserImageViewMargin * 2;
_scrollView.bounds = rect;
_scrollView.center = self.center;
CGFloat y = 0;
CGFloat w = _scrollView.frame.size.width - ZWPhotoBrowserImageViewMargin * 2;
CGFloat h = _scrollView.frame.size.height;
[_scrollView.subviews enumerateObjectsUsingBlock:^(__kindof ZWBrowserImageView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CGFloat x = ZWPhotoBrowserImageViewMargin + idx * (ZWPhotoBrowserImageViewMargin * 2 + w);
obj.frame = CGRectMake(x, y, w, h);
}];
_scrollView.contentSize = CGSizeMake(_scrollView.subviews.count * _scrollView.frame.size.width, 0);
_scrollView.contentOffset = CGPointMake(self.currentImageIndex * _scrollView.frame.size.width, 0);
//第一張圖展示的時(shí)候犬钢,還要展示首張圖片的動(dòng)畫效果
if(!_hasShowedFirstView){
[self showFirstImage];
}
_indexLabel.center = CGPointMake(self.bounds.size.width * 0.5, 35);
}
***************d苍鲜、UIWindow上展示圖片瀏覽器*******************
#pragma mark - public
//點(diǎn)擊進(jìn)入的時(shí)候是先顯示window,再執(zhí)行動(dòng)畫效果
- (void)show{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
self.frame = window.bounds;
[window addObserver:self forKeyPath:@"frame" options:0 context:nil];
[window addSubview:self];
}
好好研究下玷犹,不到一天絕對(duì)能封裝出來(lái)一個(gè)自己的圖片瀏覽器框架混滔。