【 寫在前面:筆者按照Instagram的圖片選取器寫了個(gè)小Demo,
該系列文章為筆者實(shí)現(xiàn)Demo的步驟,若有不正確的地方還望指出來(lái),共同學(xué)習(xí)。
地址:https://github.com/BigBigPo/RJPhotoPicker】
前一篇簡(jiǎn)單的介紹了PhotoKit的使用,圖片資源我們已經(jīng)拿到了,接下來(lái)就該寫選擇器了。我們先看看Instagram的圖片選擇器是什么樣子——

可以看出,界面上分為兩大塊,“展示區(qū)”與“列表區(qū)”,兩個(gè)區(qū)域具有聯(lián)動(dòng)屬性。我們先看上方的展示區(qū),這個(gè)區(qū)域最主要的功能是展示用戶當(dāng)前選中的圖片,并且具有縮放,拖動(dòng)改變圖片填充方式等功能。
看到“縮放”,“拉伸”這些字眼時(shí),筆者第一反應(yīng)就想到了UIScrollView,其完全具備上述功能,在使用中也有需要注意的事項(xiàng),下面我們就以UIScrollView來(lái)實(shí)現(xiàn)展示區(qū)的功能。
0.思考
要實(shí)現(xiàn)縮放功能很簡(jiǎn)單,Instagram的展示區(qū)還有很多細(xì)節(jié):
1、 默認(rèn)情況下,圖片將會(huì)按比例填充整個(gè)展示區(qū)(類似.ScaleAspectFill的填充效果)
2、展示區(qū)默認(rèn)中心定位在圖片中央
3、圖片的四周均具有彈簧效果。
最主要的是這三個(gè)特性,當(dāng)然還有其他次要的效果,如線框、更改填充樣式等等,這些都是錦上添花的功能,上述三個(gè)效果是其基本功能(展示,縮放)所必須具有的,權(quán)重也是最高的。
對(duì)于細(xì)節(jié)1,筆者一開始直接想到的是UIImageView的ContentMode,這個(gè)是UIImageView自身的展示方式,僅僅只是展示圖片是沒(méi)有問(wèn)題的,但現(xiàn)在需要將其加入到ScrollView中還需要達(dá)到細(xì)節(jié)2,但有點(diǎn)多余。
為什么?
細(xì)節(jié)2的實(shí)現(xiàn)上,因?yàn)槲覀兊恼故緟^(qū)域的大小是固定的,所以需要將圖片視圖進(jìn)行適配,需要獲取圖片視圖的大小,再根據(jù)contentView的大小重設(shè)UIImageView的大小來(lái)進(jìn)行適配,以此為依據(jù)來(lái)確定ScrollView的區(qū)域位置。若我們使用了UIImageView的ContentMode,我們還是要獲取圖片的大小。
這樣看來(lái),UIImageView的contentMode顯得多余了,我們會(huì)重設(shè)UIImageView的尺寸,而根據(jù)需求來(lái)看,這個(gè)重設(shè)UIImageView的尺寸就包含了我們需要的“ .ScaleAspectFill”效果。
若被繞暈了沒(méi)有關(guān)系,我們一步一步來(lái),文章最后有Demo以供參考。
接下來(lái)
1.準(zhǔn)備工作
新建一個(gè)項(xiàng)目,在場(chǎng)景中加入一個(gè)UIScrollView,并在該ScrollView中加入一個(gè)UIImageView,設(shè)置好約束(筆者用的XIB,代碼也可以,后面統(tǒng)一以XIB來(lái)講)。
因?yàn)槲覀円獙?shí)時(shí)的重置展示區(qū)的約束,所以我們需要拿到UIImageView的“寬”、“高”、“左距”、“右距”。以方便后續(xù)操作。

如上圖,界面十分簡(jiǎn)單,Next按鈕只是Demo為了切換不同尺寸的圖片。XIB中的效果如下:

2.縮放功能
單獨(dú)的縮放功能十分簡(jiǎn)單,只需要設(shè)置好UIScrollView的縮放系數(shù),以及在代理中返回需要縮放的視圖即可。
[_scrollView setDelegate:self]; //不要忘記添加代理
[_scrollView setMinimumZoomScale:1]; //最小縮放系數(shù)
[_scrollView setMaximumZoomScale:2]; //最大縮放系數(shù)
//該代理方法需要返回一個(gè)需要縮放的view,若返回nil,將沒(méi)有任何效果。
//注:在TableView、UICollectionView中使用UIScrollView來(lái)完成縮放效果時(shí),直接返回imageView可能不能達(dá)到效果,需要給imageView套一層View,在此代理中返回該view即可解決。
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _imageView;
}
單純的縮放功能就實(shí)現(xiàn)了,但圖片的大小,位置特別的奇怪,我們需要優(yōu)化一下。順便提一下,UIScrollView還提供了關(guān)于縮放更多代理,可以據(jù)此實(shí)現(xiàn)許多功能。
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view;
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
3.適配圖片的大小與位置
首先要確定圖片視圖的大小,才能更好的確定展示區(qū)具體的位置。圖片的大小適配,其實(shí)就是根據(jù)圖片的大小按照填充的類型(鋪滿,填充),保持比例縮放至容器(UIScrollView)的大小。也就是一個(gè)計(jì)算尺寸的過(guò)程。
/**
根據(jù)圖片的大小來(lái)獲得合適的容器大小
*/
- (CGSize)getImageVeiwSizeWithImage:(UIImage *)image {
//獲取scrollView的尺寸(在Demo中,我們把scrollView的長(zhǎng)寬都設(shè)置成了屏寬,是相等的)
CGFloat scrollViewSize = [UIScreen mainScreen].bounds.size.width;
//注:下方的 +1 只是為了方便實(shí)現(xiàn)ScrollView的邊界彈簧效果,并無(wú)其他用處
CGFloat width = scrollViewSize + 1;
CGFloat height = width / image.size.width * image.size.height;
if (height < scrollViewSize) {
height = scrollViewSize + 1;
width = height / image.size.height * image.size.width;
}
return CGSizeMake(width, height);
}
圖片的位置也就是UIScrollView的contentOffset,來(lái)實(shí)現(xiàn)。現(xiàn)在已經(jīng)計(jì)算出了圖片的展示尺寸,就可以知道圖片的位置(UIScrollView的contentOffset)
/**
根據(jù)圖片視圖大小來(lái)確定圖片位置
*/
- (void)setScrollViewCenterWithImageSize:(CGSize)size {
//獲取scrollView的尺寸(在Demo中,我們把scrollView的長(zhǎng)寬都設(shè)置成了屏寬,是相等的)
CGFloat scrollViewSize = [UIScreen mainScreen].bounds.size.width;
//獲取圖片中心以此為依據(jù)計(jì)算scrollView的contentOffset
CGPoint imageCenter = CGPointMake(size.width / 2, size.height / 2);
CGPoint point = CGPointMake(imageCenter.x - scrollViewSize / 2, imageCenter.y - scrollViewSize / 2);
[_scrollView setContentOffset:point];
}
展示區(qū)的主要功能到此就全部實(shí)現(xiàn)了,UIScrollView來(lái)實(shí)現(xiàn)縮放是比較簡(jiǎn)單的,代碼中我也舉出了自己遇到的一個(gè)問(wèn)題,以及解決辦法:
在UIScrollView在另一個(gè)ScrollView中時(shí),可能出現(xiàn)縮放無(wú)效的問(wèn)題,例如,UITabelView 或 UICollectionVewi的Cell中。筆者的解決辦法是在imageView外套一層View,這樣可以解決問(wèn)題,但還未知道其中的原理,也許是兩個(gè)ScrollView嵌套的問(wèn)題,或者是列表控件的內(nèi)部機(jī)制?
本節(jié)的Demo:https://github.com/BigBigPo/UIScrollViewScaleDemo