iOS 浏览相册功能实现

释放双眼,带上耳机,听听看~!

iOS 浏览相册功能实现,可缩放,画笔标记,缓存图片,记录下载进度。

首先看一下效果图:

  

  

 

下面简述下主要思路及相关代码:

HWPhotoVC(控制器,用collcetView展示缩略图,点击cell展示大图片):

#import <UIKit/UIKit.h>
@interface HWPhotoVC : UIViewController
@end
/*** ---------------分割线--------------- ***/
#import "HWPhotoVC.h"
#import "HWPhotoView.h"
#import "HWNavBar.h"
#import "HWLoadingView.h"
#import "HWPhotoManger.h"
#define KItemW 111
#define KItemH 111
#define KMargin 10
@interface HWPhotoVC ()<UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonatomic, strong) NSArray *photos;
@property (nonatomic, strong) NSArray *thumPhotos;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, weak) HWPhotoView *photoView;
@property (nonatomic, weak) HWLoadingView *loadingView;
@end
@implementation HWPhotoVC
static NSString *const reuseIdentifier = @"HWPhotosCell";
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//创建控件
[self creatControl];
}
- (void)creatControl
{
CGFloat navBarH = 64.0f;
//导航条
HWNavBar *navBar = [[HWNavBar alloc] initWithFrame:CGRectMake(0, 0, KMainW, navBarH)];
navBar.title = @"相册";
[navBar addBackButtonWithAction:@selector(photoVCBackAction)];
[self.view addSubview:navBar];
//展示视图布局
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.sectionInset = UIEdgeInsetsMake(KMargin, KMargin, KMargin, KMargin);
//展示视图
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, navBarH, KMainW, KMainH - navBarH) collectionViewLayout:flowLayout];
_collectionView.backgroundColor = [UIColor clearColor];
_collectionView.showsVerticalScrollIndicator = NO;
_collectionView.dataSource = self;
_collectionView.delegate = self;
[self.view addSubview:_collectionView];
//加载视图
HWLoadingView *loadingView = [[HWLoadingView alloc] initWithFrame:CGRectMake(0, 0, KMainW, KMainH)];
loadingView.progress = 0.0f;
weakify(self);
loadingView.back = ^() {
strongify(weakSelf);
[strongSelf photoVCBackAction];
};
[self.view addSubview:loadingView];
self.loadingView = loadingView;
//注册标识
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
//获取信息
[self getInfo];
}
- (HWPhotoView *)photoView
{
if (!_photoView) {
HWPhotoView *photoView = [[HWPhotoView alloc] initWithFrame:CGRectMake(0, 0, KMainW, KMainH)];
photoView.photos = _photos;
photoView.thumPhotos = _thumPhotos;
weakify(self);
photoView.back = ^(CGRect imageViewFrame, NSInteger item) {
strongify(weakSelf);
[strongSelf photoDismissAnimationWithFrame:imageViewFrame item:item];
};
[self.view addSubview:photoView];
_photoView = photoView;
}
return _photoView;
}
- (void)photoDismissAnimationWithFrame:(CGRect)frame item:(NSInteger)item
{
//根据item宽度计算每行可显示的个数
int colCount = (int)((KMainW - KMargin) / (KItemW + KMargin));
//创建图片并设置初始frame
UIImageView __block *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageWithContentsOfFile:[HWPhotoManger getImagePathWithImageName:_photos[item]]];
[self.view addSubview:imageView];
//点击图片时左上第一张是否能完整显示
BOOL isIntact = (int)_collectionView.contentOffset.y % (KMargin + KItemH) <= KMargin;
//点击图片时左下第一张是否能完整显示
BOOL isIntactBottom = (int)(_collectionView.contentOffset.y + _collectionView.bounds.size.height) % (KMargin + KItemH) <= KMargin;
//点击图片时左上第一张图片item
NSInteger currentFirstItem = (int)_collectionView.contentOffset.y / (KMargin + KItemH) * colCount;
//点击图片时左下第一张图片item
NSInteger currentBottomFirstItem = (int)(_collectionView.contentOffset.y + _collectionView.bounds.size.height) / (KMargin + KItemH) * colCount;
if (isIntactBottom) currentBottomFirstItem = currentBottomFirstItem - colCount;
//视图可以展示的最大item数量
NSInteger showMaxItemCount = currentBottomFirstItem - currentFirstItem + colCount;
//当结束点击图片在最上一行,并且图片不能完整显示时,下移CollectionView使图片完整显示
if (!isIntact && (item - currentFirstItem < colCount && item - currentFirstItem > - 1)) {
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
}
//当结束点击图片在最下一行,并且图片不能完整显示时,上移CollectionView使图片完整显示
if (!isIntactBottom && (item - currentFirstItem > showMaxItemCount - colCount - 1 && item - currentFirstItem < showMaxItemCount)) {
CGFloat movePadding = KItemH - ((int)(_collectionView.contentOffset.y + _collectionView.bounds.size.height) % (KItemH + KMargin) - KMargin);
[_collectionView setContentOffset:CGPointMake(0, _collectionView.contentOffset.y + movePadding)];
}
//当结束点击item小于左上第一张图片时,下移CollectionView至选中行为最上一行
if (item < currentFirstItem) {
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
}
//当结束点击item大于右下(左上第一张图片item + 可最大展示item数)时,上移CollectionView至选中行为最下一行
if (item > currentFirstItem + showMaxItemCount - 1) {
CGFloat movePadding = KItemH - ((int)(_collectionView.contentOffset.y + _collectionView.bounds.size.height) % (KItemH + KMargin) - KMargin) + (KItemH + KMargin) * (item / colCount - currentBottomFirstItem / colCount);
if (isIntactBottom) movePadding = movePadding - (KItemH + KMargin);
[_collectionView setContentOffset:CGPointMake(0, _collectionView.contentOffset.y + movePadding)];
}
//加延时确保获取到的cellFrame是collectionView移动之后的
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//获取图片缩小后的frame
UICollectionViewCell *cell = [_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0]];
//消失动画
[UIView animateWithDuration:0.25f animations:^{
imageView.frame = [self.view convertRect:cell.frame fromView:_collectionView];
}completion:^(BOOL finished) {
[imageView removeFromSuperview];
imageView = nil;
}];
});
}
- (void)getInfo
{
[_loadingView show];
//网络请求获取相册相关信息这里省略了,直接模拟获取到图片数组、缩略图数组、相册版本号、相册id
NSArray *urlArray = @[@"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/axUD1TxyazB3vSFQkDQLn5M0QPSZQB5WWLaDW0V5Xj4!/b/dGkBAAAAAAAA&bo=cQSAAgAAAAAFB9M!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/D0m2YadD0efgMEFUzxDI.VP54dmoNLE6jXG4q3Hu39Q!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFB6I!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/ajOssbYw05gPzrifwEeHYJQm63nINjpnDNk3lXV7utQ!/b/dGkBAAAAAAAA&bo=cgSAAgAAAAAFANc!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Y2bx7nPZOo*xK9CnWnEuMOIvnAOeMDLnvFGuE325tSg!/b/dGgBAAAAAAAA&bo=FQIsAQAAAAAFABk!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/yJZVj17bdMMR3sCLT1YWwNFKgK057vNjw0zL1IH6FTg!/b/dGgBAAAAAAAA&bo=4AEsAQAAAAAFAO8!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/HJUjYRFTxYElxuIP00GzRWHoymfiyylKmisGK9gbDUc!/b/dGgBAAAAAAAA&bo=VQOAAgAAAAAFAPc!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/lNbl.keTXojot43.i4eLl5EDrrrhim3Wxo225dBOQTg!/b/dGgBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/iB6SdannBkn0b8SVuTSpS6H.Mttkwj*YF.i58ge7bp4!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Li0El90EOY0E6*Haw4qtPP5i2C8dtUxxfL8RfvTTyrs!/b/dGgBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/Vtg11LWJ*osQp*WkGRAwfAi89j3Bbk5v3EZM8b3H2q0!/b/dGkBAAAAAAAA&bo=wQOAAgAAAAAFAGM!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/K2fNvk13QaUO4punmxpIAL1kZFDy4AeNRkfZmdItWyM!/b/dFYBAAAAAAAA&bo=wgH9AgAAAAAFBxg!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/7RzzUgvozabfIqwfX8rv4utG9aT3n2e3Asi4zzxvnmI!/b/dGkBAAAAAAAA&bo=gAI1AwAAAAAFB5A!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/eXRrjOn0pBOFf.7m3yaXyEztQEkoGtYcg8EBoJpaO0U!/b/dGkBAAAAAAAA&bo=yADIAAAAAAAFByQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/pw5plnw3LsocGQlRQSgrnZCPy2KV5LvWb*Nz3UNCvJQ!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFB6I!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/7EO.WlDqMcvVBYcc27XFLNplGjwdYx2p56TZNwc.Tn4!/b/dFYBAAAAAAAA&bo=ZwJnAgAAAAAFByQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/axUD1TxyazB3vSFQkDQLn5M0QPSZQB5WWLaDW0V5Xj4!/b/dGkBAAAAAAAA&bo=cQSAAgAAAAAFB9M!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/D0m2YadD0efgMEFUzxDI.VP54dmoNLE6jXG4q3Hu39Q!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFB6I!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/ajOssbYw05gPzrifwEeHYJQm63nINjpnDNk3lXV7utQ!/b/dGkBAAAAAAAA&bo=cgSAAgAAAAAFANc!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Y2bx7nPZOo*xK9CnWnEuMOIvnAOeMDLnvFGuE325tSg!/b/dGgBAAAAAAAA&bo=FQIsAQAAAAAFABk!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/yJZVj17bdMMR3sCLT1YWwNFKgK057vNjw0zL1IH6FTg!/b/dGgBAAAAAAAA&bo=4AEsAQAAAAAFAO8!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/HJUjYRFTxYElxuIP00GzRWHoymfiyylKmisGK9gbDUc!/b/dGgBAAAAAAAA&bo=VQOAAgAAAAAFAPc!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/lNbl.keTXojot43.i4eLl5EDrrrhim3Wxo225dBOQTg!/b/dGgBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/iB6SdannBkn0b8SVuTSpS6H.Mttkwj*YF.i58ge7bp4!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Li0El90EOY0E6*Haw4qtPP5i2C8dtUxxfL8RfvTTyrs!/b/dGgBAAAAAAAA&bo=AASAAgAAAAAFAKU!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/Vtg11LWJ*osQp*WkGRAwfAi89j3Bbk5v3EZM8b3H2q0!/b/dGkBAAAAAAAA&bo=wQOAAgAAAAAFAGM!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/K2fNvk13QaUO4punmxpIAL1kZFDy4AeNRkfZmdItWyM!/b/dFYBAAAAAAAA&bo=wgH9AgAAAAAFBxg!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/7RzzUgvozabfIqwfX8rv4utG9aT3n2e3Asi4zzxvnmI!/b/dGkBAAAAAAAA&bo=gAI1AwAAAAAFB5A!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/eXRrjOn0pBOFf.7m3yaXyEztQEkoGtYcg8EBoJpaO0U!/b/dGkBAAAAAAAA&bo=yADIAAAAAAAFByQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/pw5plnw3LsocGQlRQSgrnZCPy2KV5LvWb*Nz3UNCvJQ!/b/dGkBAAAAAAAA&bo=AASAAgAAAAAFB6I!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/7EO.WlDqMcvVBYcc27XFLNplGjwdYx2p56TZNwc.Tn4!/b/dFYBAAAAAAAA&bo=ZwJnAgAAAAAFByQ!&rf=viewer_4"];
NSArray *thumUrlArray = @[@"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/oDM0rERpn5N83a2QFuXUHu8L*xSgOy0z7lcjNxiGZW4!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFAOE!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/q*CPyLxw3XhsNW71p.iNXZ0cY53CpFn884sLFYkj1cs!/b/dGkBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/1PTuUpFR*aLa2mT6Lxgq1f3TrDAsDF.7wlAvjgLjUvQ!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFB.Y!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/FGhSAze1L1rYWEWU4cA45zTtJYp4rvsSSuOM1TvAWT8!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFAOE!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/uR*1kuC7oM12uYffpYboQwDNyPUA8Q.9xLNbyMkx.zE!/b/dGYBAAAAAAAA&bo=lgBeAAAAAAAFAOs!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/iI1t618fi94LzYwkJk0zRX8SMQWr8XGHQ98ioZYhUKM!/b/dGkBAAAAAAAA&bo=lgBxAAAAAAAFAMQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/tEfmzbb22KtoJwohbUIkNXG9U3348pk7cI1HdlDD3OE!/b/dGkBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/fc0EInMMxEzeMqRUfr4n8oDjvjxRSKkAxi0.UTwMcuQ!/b/dGgBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/0kKI.OxxnonRO*PamJT8IrvEqFL0YX254sbitqXU9z0!/b/dGgBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/4QPH9RB7lBVeZNbUsp0xXUAPm1Iku1Dz*9sdvrVDEns!/b/dGgBAAAAAAAA&bo=lgBkAAAAAAAFB9Y!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/xkR2EkWlq.gBmIq0cWKoUG1qrvl86lDljdBeHRENlug!/b/dGkBAAAAAAAA&bo=WACWAAAAAAAFB.o!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/8jl5H11.QX4iGBJ3j6JS2Fvoy.WlG6IVcoNCEzKMVBU!/b/dGkBAAAAAAAA&bo=dQCWAAAAAAAFB8c!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Xwn7NLeHycrnrLJ.w4hzcmq9QmS7FW09gYXsxtLPORc!/b/dGgBAAAAAAAA&bo=lgCWAAAAAAAFByQ!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/*lg6dH6TTg.qe4HttnA1v4J7G3ucQeJ2s06xuVc1zgU!/b/dFYBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/pfhX5nA5usc0IqoFItZMJT4EgYloUC8bd5cElsp4FI8!/b/dGkBAAAAAAAA&bo=lgCWAAAAAAAFByQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/oDM0rERpn5N83a2QFuXUHu8L*xSgOy0z7lcjNxiGZW4!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFAOE!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/q*CPyLxw3XhsNW71p.iNXZ0cY53CpFn884sLFYkj1cs!/b/dGkBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/1PTuUpFR*aLa2mT6Lxgq1f3TrDAsDF.7wlAvjgLjUvQ!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFB.Y!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/FGhSAze1L1rYWEWU4cA45zTtJYp4rvsSSuOM1TvAWT8!/b/dGkBAAAAAAAA&bo=lgBUAAAAAAAFAOE!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/uR*1kuC7oM12uYffpYboQwDNyPUA8Q.9xLNbyMkx.zE!/b/dGYBAAAAAAAA&bo=lgBeAAAAAAAFAOs!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/iI1t618fi94LzYwkJk0zRX8SMQWr8XGHQ98ioZYhUKM!/b/dGkBAAAAAAAA&bo=lgBxAAAAAAAFAMQ!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/tEfmzbb22KtoJwohbUIkNXG9U3348pk7cI1HdlDD3OE!/b/dGkBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/fc0EInMMxEzeMqRUfr4n8oDjvjxRSKkAxi0.UTwMcuQ!/b/dGgBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/0kKI.OxxnonRO*PamJT8IrvEqFL0YX254sbitqXU9z0!/b/dGgBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/4QPH9RB7lBVeZNbUsp0xXUAPm1Iku1Dz*9sdvrVDEns!/b/dGgBAAAAAAAA&bo=lgBkAAAAAAAFB9Y!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/xkR2EkWlq.gBmIq0cWKoUG1qrvl86lDljdBeHRENlug!/b/dGkBAAAAAAAA&bo=WACWAAAAAAAFB.o!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/8jl5H11.QX4iGBJ3j6JS2Fvoy.WlG6IVcoNCEzKMVBU!/b/dGkBAAAAAAAA&bo=dQCWAAAAAAAFB8c!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/Xwn7NLeHycrnrLJ.w4hzcmq9QmS7FW09gYXsxtLPORc!/b/dGgBAAAAAAAA&bo=lgCWAAAAAAAFByQ!&rf=viewer_4", @"http://a1.qpic.cn/psb?/V13GwxkT2AqZnq/*lg6dH6TTg.qe4HttnA1v4J7G3ucQeJ2s06xuVc1zgU!/b/dFYBAAAAAAAA&bo=lgBeAAAAAAAFB.w!&rf=viewer_4", @"http://a2.qpic.cn/psb?/V13GwxkT2AqZnq/pfhX5nA5usc0IqoFItZMJT4EgYloUC8bd5cElsp4FI8!/b/dGkBAAAAAAAA&bo=lgCWAAAAAAAFByQ!&rf=viewer_4"];
//相册版本号,相册内部有更新时更新版本号,这样做的缺点是需要全部更新,如果更新频繁还是单张缓存吧,有很多优秀的开源库
NSString *newVersion = @"1.0.0";
//相册id,本地缓存路径是通过这个id拼接的,多个相册不会互绕
NSString *photoID = @"1000";
//获取本保存的相册版本号
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *currentVersion = [defaults objectForKey:[HWPhotoManger getPhotoVersionKeyWithPhotoID:photoID]];
//如果有本地缓存
if ([newVersion isEqualToString:currentVersion]) {
//取本地图片
_photos = [NSKeyedUnarchiver unarchiveObjectWithFile:[HWPhotoManger getPhotoPathWithPhotoID:photoID]];
_thumPhotos = [NSKeyedUnarchiver unarchiveObjectWithFile:[HWPhotoManger getThumPhotoPathWithPhotoID:photoID]];
//刷新
[_loadingView dismiss];
[_collectionView reloadData];
//如果没有本地缓存
}else {
HWPhotoManger *photoManger = [HWPhotoManger sharePhotoManger];
//加载图片
if (!photoManger.isLoading) {
[photoManger getImageWithUrlArray:urlArray thumUrlArray:thumUrlArray photoID:photoID version:newVersion];
}else {
_loadingView.progress = photoManger.progress;
}
//加载失败
photoManger.error = ^() {
NSLog(@"加载失败,请检查网络");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self photoVCBackAction];
});
};
//刷新进度
photoManger.progressBlock = ^(CGFloat progress) {
_loadingView.progress = progress;
};
//刷新视图
photoManger.finishBlock = ^(NSArray *array, NSArray *thumArray) {
_photos = array;
_thumPhotos = thumArray;
[_loadingView dismiss];
[_collectionView reloadData];
};
}
}
- (void)photoVCBackAction
{
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _thumPhotos.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[HWPhotoManger getImagePathWithImageName:_thumPhotos[indexPath.item]]]];
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(KItemW, KItemH);
}
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
CGRect selectPhotoFrame = [self.view convertRect:cell.frame fromView:collectionView];
[self.photoView showWithSelectPhotoFrame:selectPhotoFrame selectItem:indexPath.item];
}
@end

 

HWPhotoView(图片展示视图,用scrollView做为容器展示图片,设置缩放、点击、滑动事件,添加了一个collectionView做为快速浏览视图,画笔功能是添加了一个空视图覆盖在图片上,并未与图片融合):

#import <UIKit/UIKit.h>
@interface HWPhotoView : UIView
@property (nonatomic, strong) NSArray *photos;
@property (nonatomic, strong) NSArray *thumPhotos;
@property (nonatomic, copy) void(^ back)(CGRect imageFrame, NSInteger item);
- (void)showWithSelectPhotoFrame:(CGRect)selectPhotoFrame selectItem:(NSInteger)selectItem;
@end
/*** ---------------分割线--------------- ***/
#import "HWPhotoView.h"
#import "HWPaintView.h"
#import "HWPaintBar.h"
#import "HWPhotoManger.h"
#import "HWNavBar.h"
#define KHWPhotoViewMargin 2
#define KCollectionViewHeigth 100
#define KPicMinScale 1.0
#define KPicMaxScale 2.4
#define KItemW 70
#define KItemH 96
@interface HWPhotoView ()<UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate, HWPaintBarDelegate>
@property (nonatomic, weak) UILabel *label;
@property (nonatomic, weak) UIImageView *imageView;
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, weak) HWNavBar *navBar;
@property (nonatomic, weak) HWPaintView *paintView;
@property (nonatomic, weak) HWPaintBar *paintBar;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, assign) CGFloat startCollectionViewX;
@property (nonatomic, assign) CGFloat lastScrContX;
@end
@implementation HWPhotoView
static NSString *const reuseIdentifier = @"HWPhotoViewCell";
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor blackColor];
//容器
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
scrollView.minimumZoomScale = KPicMinScale;
scrollView.maximumZoomScale = KPicMaxScale;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.delegate = self;
[self addSubview:scrollView];
self.scrollView = scrollView;
//单击
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(click)];
tap.numberOfTouchesRequired = 1;
tap.numberOfTapsRequired = 1;
[scrollView addGestureRecognizer:tap];
//双击
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleClick)];
tap2.numberOfTapsRequired = 2;
[scrollView addGestureRecognizer:tap2];
[tap requireGestureRecognizerToFail:tap2];
//右滑手势
UISwipeGestureRecognizer *rightSwip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(nextPage)];
rightSwip.direction = UISwipeGestureRecognizerDirectionLeft;
[scrollView addGestureRecognizer:rightSwip];
//左滑手势
UISwipeGestureRecognizer *leftSwip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(forwardPage)];
leftSwip.direction = UISwipeGestureRecognizerDirectionRight;
[scrollView addGestureRecognizer:leftSwip];
//下滑手势
UISwipeGestureRecognizer *downSwip = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(downSwipAction)];
downSwip.direction = UISwipeGestureRecognizerDirectionDown;
[scrollView addGestureRecognizer:downSwip];
//图片
UIImageView *imageView = [[UIImageView alloc] init];
[self.scrollView addSubview:imageView];
self.imageView = imageView;
//手绘板
HWPaintView *paintView = [[HWPaintView alloc] initWithFrame:self.bounds];
paintView.hidden = YES;
[self addSubview:paintView];
self.paintView = paintView;
//导航条
HWNavBar *navBar = [[HWNavBar alloc] initWithFrame:CGRectMake(0, 0, KMainW, 64)];
navBar.title = @"相册";
[navBar addBackButtonWithAction:@selector(photoViewBackAction)];
[self addSubview:navBar];
self.navBar = navBar;
//开启手绘按钮
UIButton *paintBtn = [[UIButton alloc] initWithFrame:CGRectMake(KMainW - 95, 20, 80, 44)];
paintBtn.selected = YES;
[paintBtn setTitle:@"显示画笔" forState:UIControlStateNormal];
[paintBtn setTitle:@"隐藏画笔" forState:UIControlStateSelected];
[paintBtn addTarget:self action:@selector(paintBtnOnClick:) forControlEvents:UIControlEventTouchUpInside];
[navBar addSubview:paintBtn];
//画笔工具条
HWPaintBar *paintBar = [[HWPaintBar alloc] initWithFrame:CGRectMake(KMainW - 60, 80, 44, 412)];
paintBar.delegate = self;
[self addSubview:paintBar];
self.paintBar = paintBar;
//布局
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumLineSpacing = KHWPhotoViewMargin;
layout.sectionInset = UIEdgeInsetsMake(KHWPhotoViewMargin, KHWPhotoViewMargin, KHWPhotoViewMargin, KHWPhotoViewMargin);
//浏览条
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, KMainH - KCollectionViewHeigth, KMainW, KCollectionViewHeigth) collectionViewLayout:layout];
_collectionView.backgroundColor = [UIColor colorWithHexString:@"003c65"];
_collectionView.dataSource = self;
_collectionView.delegate = self;
[self addSubview:_collectionView];
//注册标识
[_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
}
return self;
}
//左滑事件
- (void)nextPage
{
if (_index < _photos.count - 1) {
_index ++;
//当滑动展示视图超出展示范围,未选中时进行翻页,先恢复到原位置
[_collectionView setContentOffset:CGPointMake(_startCollectionViewX, 0)];
//滑动时展示图最右边图片是否能完全显示
BOOL isIntactRight = (int)(_collectionView.contentOffset.x + KMainW) % (KHWPhotoViewMargin + KItemW) <= KHWPhotoViewMargin;
//滑动时展示图最右边图片item
NSInteger currentRightItem = (int)(_collectionView.contentOffset.x + KMainW) / (KHWPhotoViewMargin + KItemW);
if (isIntactRight) currentRightItem--;
//如果最右边item不能完全显示,并滑动到该item,左移视图至item完全显示
if (!isIntactRight && _index == currentRightItem) {
CGFloat movePadding = KItemW - ((int)(_collectionView.contentOffset.x + KMainW) % (KItemW + KHWPhotoViewMargin) - KHWPhotoViewMargin);
[_collectionView setContentOffset:CGPointMake(_collectionView.contentOffset.x + movePadding, 0)];
}
//如果滑动超出展示视图最大item,左移视图至item显示
if (_index > currentRightItem) {
CGFloat movePadding = KItemW - ((int)(_collectionView.contentOffset.x + KMainW) % (KItemW + KHWPhotoViewMargin) - KHWPhotoViewMargin) + (KItemW + KHWPhotoViewMargin);
if (isIntactRight) movePadding = movePadding - (KItemW + KHWPhotoViewMargin);
[_collectionView setContentOffset:CGPointMake(_collectionView.contentOffset.x + movePadding, 0)];
}
//刷新视图
[self reloadPhotoView];
[self transitionWithType:@"pageCurl" WithSubtype:kCATransitionFromRight ForView:self];
}else {
NSLog(@"已经是最后一页了");
}
}
//右滑事件
- (void)forwardPage
{
if (_index > 0) {
_index --;
//当滑动展示视图超出展示范围,未选中时进行翻页,先恢复到原位置
[_collectionView setContentOffset:CGPointMake(_startCollectionViewX, 0)];
//滑动时展示图最左边图片是否能完全显示
BOOL isIntactLeft = (int)_collectionView.contentOffset.x % (KHWPhotoViewMargin + KItemW) <= KHWPhotoViewMargin;
//滑动时展示图最左边图片item
NSInteger currentLeftItem = (int)_collectionView.contentOffset.x / (KHWPhotoViewMargin + KItemW);
//如果最左边item不能完全显示,并滑动到该item,右移视图至item完全显示
if (!isIntactLeft && _index == currentLeftItem) {
CGFloat movePadding = (int)_collectionView.contentOffset.x % (KItemW + KHWPhotoViewMargin);
[_collectionView setContentOffset:CGPointMake(_collectionView.contentOffset.x - movePadding, 0)];
}
//如果滑动超出展示视图最小item,右移视图至item显示
if (_index < currentLeftItem) {
CGFloat movePadding = (int)_collectionView.contentOffset.x % (KItemW + KHWPhotoViewMargin) + (KItemW + KHWPhotoViewMargin);
[_collectionView setContentOffset:CGPointMake(_collectionView.contentOffset.x - movePadding, 0)];
}
//刷新视图
[self reloadPhotoView];
[self transitionWithType:@"pageUnCurl" WithSubtype:kCATransitionFromRight ForView:self];
}else {
NSLog(@"已经是第一页了");
}
}
//下滑事件
- (void)downSwipAction
{
[self dismiss];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
if (_back) _back(_imageView.frame, _index);
}
//更新视图信息
- (void)reloadPhotoView
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self reloadSelectItem];
});
_scrollView.zoomScale = KPicMinScale;
_imageView.image = [UIImage imageWithContentsOfFile:[HWPhotoManger getImagePathWithImageName:_photos[_index]]];
[self setImageViewFrame];
_label.text = [NSString stringWithFormat:@"%ld / %ld", _index + 1, _photos.count];
}
//更新展示视图选中项
- (void)reloadSelectItem
{
_startCollectionViewX = _collectionView.contentOffset.x;
UICollectionViewCell *cell;
for (int i = 0; i < _photos.count; i++) {
cell = [_collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
cell.backgroundView.layer.borderWidth = i == _index ? 5.0f : 0.0f;
}
}
//设置翻页动画效果
- (void)transitionWithType:(NSString *)type WithSubtype:(NSString *)subtype ForView:(UIView *)view
{
CATransition *animation = [CATransition animation];
animation.duration = 0.7f;
animation.type = type;
if (subtype != nil) {
animation.subtype = subtype;
}
animation.timingFunction = UIViewAnimationOptionCurveEaseInOut;
[view.layer addAnimation:animation forKey:@"animation"];
}
//单击屏幕显示隐藏菜单
- (void)click
{
if (_navBar.hidden == YES) {
_navBar.hidden = NO;
_collectionView.hidden = NO;
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
}else {
_navBar.hidden = YES;
_collectionView.hidden = YES;
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
}
}
//双击屏幕放大缩小图片
- (void)doubleClick
{
[UIView animateWithDuration:0.25f animations:^{
_scrollView.zoomScale = _scrollView.zoomScale == KPicMinScale ? KPicMaxScale : KPicMinScale;
}];
}
//返回按钮点击事件
- (void)photoViewBackAction
{
[self dismiss];
if (_back) _back(_imageView.frame, _index);
}
//开启编辑按钮点击事件
- (void)paintBtnOnClick:(UIButton *)btn
{
btn.selected = !btn.selected;
_paintBar.hidden = !btn.selected;
}
//显示
- (void)showWithSelectPhotoFrame:(CGRect)selectPhotoFrame selectItem:(NSInteger)selectItem
{
self.hidden = NO;
_index = selectItem;
_label.text = [NSString stringWithFormat:@"%ld / %ld", _index + 1, _photos.count];
[_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_index inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self reloadSelectItem];
});
_imageView.image = [UIImage imageWithContentsOfFile:[HWPhotoManger getImagePathWithImageName:_photos[_index]]];
_imageView.frame = selectPhotoFrame;
[UIView animateWithDuration:0.25f animations:^{
[self setImageViewFrame];
}];
}
//更新图片frame
- (void)setImageViewFrame
{
CGRect frame = _imageView.frame;
frame.size.width = _imageView.image.size.width > KMainW ? KMainW : _imageView.image.size.width;
frame.size.height = frame.size.width * (_imageView.image.size.height / _imageView.image.size.width);
if (frame.size.height > KMainH) {
frame.size.height = KMainH;
frame.size.width = KMainH * (_imageView.image.size.width / _imageView.image.size.height);
}
frame.origin.x = (KMainW - frame.size.width) * 0.5;
frame.origin.y = (KMainH - frame.size.height) * 0.5;
_imageView.frame = frame;
}
//隐藏
- (void)dismiss
{
_scrollView.zoomScale = KPicMinScale;
self.hidden = YES;
}
#pragma mark - HWPaintBarDelegate
- (void)didClickClearBtnInHWPaintBar:(HWPaintBar *)paintBar
{
[_paintView clear];
}
- (void)didClickCancelBtnInHWPaintBar:(HWPaintBar *)paintBar
{
[_paintView cancel];
}
- (void)paintBar:(HWPaintBar *)paintBar didClickBrushBtnWithState:(BOOL)isOpen
{
_paintView.hidden = !isOpen;
if (!isOpen) [_paintView clear];
}
- (void)paintBar:(HWPaintBar *)paintBar didClickStateBtnWithState:(int)state
{
_paintView.lineState = state;
}
- (void)paintBar:(HWPaintBar *)paintBar didClickColorBtnWithColor:(UIColor *)color
{
_paintView.lineColor = color;
}
#pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return _thumPhotos.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:[HWPhotoManger getImagePathWithImageName:_thumPhotos[indexPath.item]]]];
cell.backgroundView.layer.borderColor = [[UIColor colorWithHexString:@"#00BFFF"] CGColor];
if (indexPath.item == _index) cell.backgroundView.layer.borderWidth = 5.0f;
return cell;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(KItemW, KItemH);
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
_index = indexPath.item;
[_paintView clear];
[self reloadPhotoView];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
CGFloat offsetX = (scrollView.bounds.size.width > scrollView.contentSize.width) ? (scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5 : 0.0;
CGFloat offsetY = (scrollView.bounds.size.height > scrollView.contentSize.height) ? (scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5 : 0.0;
self.imageView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, scrollView.contentSize.height * 0.5 + offsetY);
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.imageView;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.isZooming) return;
//设置允许翻页的偏移量
CGFloat movePadding = 70;
//仿苹果原生相册,图片放大后,滑动前在边界时才允许翻页,这里加了±10的偏移量
if (scrollView.contentOffset.x < - movePadding && self.lastScrContX < 10) {
_scrollView.zoomScale = KPicMinScale;
[self forwardPage];
}
if (scrollView.contentSize.width - scrollView.contentOffset.x < KMainW - movePadding && scrollView.contentSize.width != 0 && fabs(self.lastScrContX + KMainW - scrollView.contentSize.width) < 10) {
_scrollView.zoomScale = KPicMinScale;
[self nextPage];
}
}
//滑动自然停止时调用
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
//记录滑动停止时的偏移量
self.lastScrContX = scrollView.contentOffset.x;
}
//滑动手动停止时调用
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
self.lastScrContX = scrollView.contentOffset.x;
}
@end

 

HWPaintView(绘画视图):

 

#import <UIKit/UIKit.h>
typedef enum {
HWPaintLineStateNormal = 0,
HWPaintLineStateLight,
} HWPaintLineState;
@interface HWPaintView : UIView
@property (nonatomic, assign) HWPaintLineState lineState;
@property (nonatomic, strong) UIColor *lineColor;
- (void)clear;
- (void)cancel;
@end
/*** ---------------分割线--------------- ***/
#import "HWPaintView.h"
@interface HWPaintView ()
@property (nonatomic, strong) NSMutableArray *paths;
@end
@implementation HWPaintView
- (NSMutableArray *)paths
{
if (!_paths) {
_paths = [NSMutableArray array];
}
return _paths;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
self.lineColor = [UIColor colorWithHexString:@"#fa4d32"];
}
return self;
}
- (void)clear
{
[self.paths removeAllObjects];
[self setNeedsDisplay];
}
- (void)cancel
{
[self.paths removeLastObject];
[self setNeedsDisplay];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint startPoint = [touch locationInView:touch.view];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
path.lineWidth = 5;
[path moveToPoint:startPoint];
[path addLineToPoint:startPoint];
[self.paths addObject:path];
[self setNeedsDisplay];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:touch.view];
UIBezierPath *path = [self.paths lastObject];
[path addLineToPoint:currentPoint];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
for (UIBezierPath *path in self.paths) {
if (self.lineState == HWPaintLineStateNormal) {
[self.lineColor set];
}else if (self.lineState == HWPaintLineStateLight) {
[[UIColor whiteColor] set];
CGContextSetShadowWithColor(UIGraphicsGetCurrentContext(), CGSizeMake(0, 0), 8, [self.lineColor CGColor]);
}
[path stroke];
}
}
@end

 

HWPhotoManger(图片加载缓存管理类,在这里获取图片、路径、进度):

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface HWPhotoManger : NSObject
@property (nonatomic, copy) void(^progressBlock)(CGFloat progress);
@property (nonatomic, copy) void(^finishBlock)(NSArray *array, NSArray *thumArray);
@property (nonatomic, copy) void(^error)();
@property (nonatomic, assign, readonly) BOOL isLoading;
@property (nonatomic, assign) CGFloat progress;
+ (instancetype)sharePhotoManger;
+ (NSString *)getPhotoVersionKeyWithPhotoID:(NSString *)photoID;
+ (NSString *)getPhotoPathWithPhotoID:(NSString *)photoID;
+ (NSString *)getThumPhotoPathWithPhotoID:(NSString *)photoID;
+ (NSString *)getImagePathWithImageName:(NSString *)imageName;
- (void)getImageWithUrlArray:(NSArray *)urlArray thumUrlArray:(NSArray *)thumUrlArray photoID:(NSString *)photoID version:(NSString *)version;
@end
/*** ---------------分割线--------------- ***/
#import "HWPhotoManger.h"
#define KPhotoCachesPath [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]
@interface HWPhotoManger ()
@property (nonatomic, assign, readwrite) BOOL isLoading;
@end
@implementation HWPhotoManger
+ (instancetype)sharePhotoManger
{
static HWPhotoManger *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (void)getImageWithUrlArray:(NSArray *)urlArray thumUrlArray:(NSArray *)thumUrlArray photoID:(NSString *)photoID version:(NSString *)version
{
_isLoading = YES;
if (_progressBlock == nil) _progressBlock = ^(CGFloat progress) {};
if (_finishBlock == nil) _finishBlock = ^(NSArray *array, NSArray *thumArray) {};
if (_error == nil) _error = ^(){};
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *versionKey = [NSString stringWithFormat:@"photosVersionKey%@", photoID];
//本地相册路径(原图)
NSString *photoName = [@"HWPhotosCache" stringByAppendingString:photoID];
NSString *photoPath = [KPhotoCachesPath stringByAppendingPathComponent:photoName];
//本地相册路径(缩略图)
NSString *thumPhotoName = [@"HWThumPhotosCache" stringByAppendingString:photoID];
NSString *thumPhotoPath = [KPhotoCachesPath stringByAppendingPathComponent:thumPhotoName];
NSMutableArray *temArr = [NSMutableArray array];
NSMutableArray *thumTemArr = [NSMutableArray array];
__block int i = 0;
weakify(self);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
strongify(weakSelf);
for (NSString *url in urlArray) {
//加载图片(这个方式并不好)
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
if (image == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.error();
strongSelf.isLoading = NO;
strongSelf.progress = 0;
});
return;
}
//将图片保存到本地
NSString *imageName = [NSString stringWithFormat:@"%@image%d", photoName, i];
NSString *iamgePath = [KPhotoCachesPath stringByAppendingPathComponent:imageName];
[UIImagePNGRepresentation(image) writeToFile:iamgePath atomically:YES];
[temArr addObject:imageName];
UIImage *thumImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:thumUrlArray[i++]]]];
if (thumImage == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.error();
strongSelf.isLoading = NO;
strongSelf.progress = 0;
});
return;
}
NSString *thumImageName = [NSString stringWithFormat:@"%@thumImage%d", thumPhotoName, i];
NSString *thumIamgePath = [KPhotoCachesPath stringByAppendingPathComponent:thumImageName];
[UIImagePNGRepresentation(thumImage) writeToFile:thumIamgePath atomically:YES];
[thumTemArr addObject:thumImageName];
dispatch_async(dispatch_get_main_queue(), ^{
strongSelf.progress = (CGFloat)thumTemArr.count / urlArray.count;
strongSelf.progressBlock(strongSelf.progress);
if (urlArray.count == thumTemArr.count) {
strongSelf.finishBlock(temArr, thumTemArr);
strongSelf.isLoading = NO;
strongSelf.progress = 0;
//保存
[defaults setObject:version forKey:versionKey];
[NSKeyedArchiver archiveRootObject:temArr toFile:photoPath];
[NSKeyedArchiver archiveRootObject:thumTemArr toFile:thumPhotoPath];
}
});
}
});
}
+ (NSString *)getPhotoVersionKeyWithPhotoID:(NSString *)photoID
{
return [NSString stringWithFormat:@"photosVersionKey%@", photoID];
}
+ (NSString *)getPhotoPathWithPhotoID:(NSString *)photoID
{
return [KPhotoCachesPath stringByAppendingPathComponent:[@"HWPhotosCache" stringByAppendingString:photoID]];
}
+ (NSString *)getThumPhotoPathWithPhotoID:(NSString *)photoID
{
return [KPhotoCachesPath stringByAppendingPathComponent:[@"HWThumPhotosCache" stringByAppendingString:photoID]];
}
+ (NSString *)getImagePathWithImageName:(NSString *)imageName
{
return [KPhotoCachesPath stringByAppendingPathComponent:imageName];
}
@end

还有几个自定义的视图不贴出来了。

相册Demo下载链接:http://code.cocoachina.com/view/134624

写博客的初心是希望大家共同交流成长,博主水平有限难免有偏颇之处,欢迎批评指正。

人已赞赏
iOS文章

iOS 进度条、加载、安装动画

2020-2-23 4:19:23

iOS文章加密

iOS开发MD5加密

2020-2-23 6:19:43

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索