iOS 加載網(wǎng)絡(luò)圖片緩存本地實現(xiàn)瀑布流(無需客服端提供圖片尺寸)以及點擊圖片放大功能

好久沒寫技術(shù)博客了,最近遇到一個需求瀑布流方式實現(xiàn)商品詳情展示,并且圖片尺寸各種各樣都有,且后端不返回圖片尺寸,這種條件下實現(xiàn)瀑布流還是相當(dāng)困難的,在網(wǎng)上找了很多博客都沒有達(dá)到想要的效果,所以有必要記錄一下。大家都知道瀑布流實現(xiàn)的核心就是圖片尺寸。如果是本地圖片很容易計算得到圖片尺寸(所謂本地圖片實現(xiàn)瀑布流基本沒什么用),但是網(wǎng)絡(luò)圖片就比較困難了,首先需要把圖片異步緩存到本地,然后計算每張圖片的尺寸,最后進(jìn)行瀑布流布局展示。為了更加實用直接加上點擊圖片放大功能。一般這樣的瀑布流都是用于詳情的展示界面,點擊放大以及各種手勢肯定是要有的。

先上幾張效果圖:

好了直接上代碼吧

首先是關(guān)于網(wǎng)絡(luò)圖片加載緩存本地

////? UIView+MZwebCache.h

//? Gray_main//

//? Created by CE on 17/5/22.

//? Copyright ? 2017年 CE. All rights reserved.

//#importtypedef void (^MZwebCacheBlock)(UIImage *image, BOOL bFromCache, NSError *error);

@interface UIView (MZwebCache)

- (void)setImageWithUrl:(NSURL *)url

placeHolder:(UIImage *)holderImage

completion:(MZwebCacheBlock)block;

- (void)setImageWithUrl:(NSURL *)url placeHolder:(UIImage *)holderImage;

- (void)setImageWithUrl:(NSURL *)url;

@end

@interface CachedImageManager : NSObject

+ (CachedImageManager *)shareInstance;

- (void)clearCache;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //清除緩存

- (BOOL)cacheUrl:(NSURL *)url WithData:(NSData *)data; //存入url

- (NSString *)imagePathForUrl:(NSURL *)url;? ? ? ? ? ? //取出url對應(yīng)的path

@property (nonatomic, copy, readonly) NSString *cachePath; //緩存目錄

@end

////? UIView+MZwebCache.m

//? Gray_main//

//? Created by CE on 17/5/22.

//? Copyright ? 2017年 CE. All rights reserved.

//#import "UIView+MZwebCache.h"

#import//用于MD5

@implementation UIView (MZwebCache)

- (void)setImageWithUrl:(NSURL *)url

placeHolder:(UIImage *)holderImage

completion:(MZwebCacheBlock)block {

__weak typeof(self) weakSelf = self;

@autoreleasepool {

//去找真實圖片

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 1.搜索對應(yīng)文件名

NSString *savedName = [[CachedImageManager shareInstance] imagePathForUrl:url];

// 2.如存在,則直接block;如果不存在,下載

if (savedName) {

UIImage *image = [UIImage imageWithContentsOfFile:savedName];

dispatch_async(dispatch_get_main_queue(), ^{

[weakSelf showImage:image];

if (block) {

block(image, YES, nil);

}

});

}

else {

if (url == nil) {

NSLog(@"圖片地址為空");

return ;

}

//先加載holder

holderImage ? [weakSelf showImage:holderImage] : nil;

NSError *error = nil;

NSData *imageData = [[NSData alloc] initWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:&error];

if (error) { //下載失敗

if (block) {

block(nil, NO, error);

}

}

else { //下載成功

UIImage *image = [UIImage imageWithData:imageData];

dispatch_async(dispatch_get_main_queue(), ^{

[weakSelf showImage:image];

if (block) {

block(image, NO, nil);

}

});

//緩存

if (![[CachedImageManager shareInstance] cacheUrl:url WithData:imageData]) {

NSLog(@"緩存失敗");

}

}

}

});

}

}

- (void)setImageWithUrl:(NSURL *)url placeHolder:(UIImage *)holderImage {

[self setImageWithUrl:url placeHolder:holderImage completion:nil];

}

- (void)setImageWithUrl:(NSURL *)url {

[self setImageWithUrl:url placeHolder:nil completion:nil];

}

//設(shè)置圖片到控件上

- (void)showImage:(UIImage *)image {

if ([self isKindOfClass:[UIImageView class]]) {

UIImageView *temp = (UIImageView *)self;

[temp setImage:image];

} else if ([self isKindOfClass:[UIButton class]]) {

UIButton *temp = (UIButton *)self;

[temp setBackgroundImage:image forState:UIControlStateNormal];

temp.contentMode = UIViewContentModeScaleAspectFill;

temp.layer.masksToBounds = YES;

}

}

@end

#pragma mark - 已緩存圖片文件管理

static dispatch_once_t once;

static CachedImageManager *manager = nil;

@interface

CachedImageManager () {

NSString *plistPath;? ? ? ? //存儲的plist路徑

NSFileManager *fileManager; //文件管理器

NSMutableDictionary *plistContent; // plist里存儲的內(nèi)容

NSDateFormatter *format; // date類型

}

@end

#define plistCacheName @"imageCache.plist"

@implementation CachedImageManager

+ (CachedImageManager *)shareInstance {

dispatch_once(&once, ^{

manager = [[CachedImageManager alloc] init];

});

return manager;

}

- (id)init {

self = [super init];

if (self) {

format = [[NSDateFormatter alloc] init];

format.dateFormat = @"yyyyMMdd-hhmmss";

plistContent = [NSMutableDictionary dictionary];

fileManager = [NSFileManager defaultManager];

_cachePath

= [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,

NSUserDomainMask, YES)[0] stringByAppendingPathComponent:@"ZMZCache"];

//如果不存在文件夾,則創(chuàng)建

if (![fileManager fileExistsAtPath:_cachePath]) {

NSError *error = nil;

BOOL isok = [fileManager createDirectoryAtPath:_cachePath withIntermediateDirectories:YES attributes:nil error:&error];

if (!isok) {

NSLog(@"%@", error);

}

}

plistPath = [_cachePath stringByAppendingPathComponent:plistCacheName];

NSLog(@"%@", plistPath);

//如果不存在plist文件,則創(chuàng)建

if (![fileManager fileExistsAtPath:plistPath]) {

[fileManager createFileAtPath:plistPath contents:nil attributes:nil];

} else {

//讀取plist內(nèi)容

plistContent = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];

}

}

return self;

}

#pragma mark - 清理緩存

- (void)clearCache {

NSError *error;

if ([fileManager removeItemAtPath:_cachePath error:&error]) {

NSLog(@"清除image緩存成功");

} else {

NSLog(@"清除image緩存失敗,原因:%@", error);

}

}

#pragma mark - 緩存文件到本地

- (BOOL)cacheUrl:(NSURL *)url WithData:(NSData *)data {

//計算名字

NSString *cacheString = [self caculateNameForKey:url.absoluteString];

NSString *writePath = [_cachePath stringByAppendingPathComponent:cacheString];

//寫入

[data writeToFile:writePath atomically:NO];

[plistContent setValue:cacheString forKey:url.absoluteString];

[plistContent writeToFile:plistPath atomically:NO];

return YES;

}

#pragma mark - url圖片對應(yīng)名稱

- (NSString *)imagePathForUrl:(NSURL *)url {

id searchResult = [plistContent valueForKey:url.absoluteString];

if (searchResult) {

return [_cachePath stringByAppendingPathComponent:searchResult];

}

return nil;

}

#pragma mark - 計算緩存名稱

- (NSString *)caculateNameForKey:(NSString *)key {

const char *str = [key UTF8String];

if (str == NULL) {

str = "";

}

unsigned char r[CC_MD5_DIGEST_LENGTH];

CC_MD5(str, (CC_LONG) strlen(str), r);

NSString *filename = [NSString

stringWithFormat:

@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@", r[0], r[1],

r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14],

r[15], [format stringFromDate:[NSDate date]]];

return filename;

}

@end

布局和計算

粘貼的代碼格式亂了,還是直接上圖吧。

////? WaterFlowLayout.m

//? Gray_main

//? Created by CE on 17/5/20.//

Copyright ? 2017年 CE. All rights reserved.

//#import "WaterFlowLayout.h"#define preloadHeight 100

? //豫加載上下各100@interface WaterFlowLayout ()

//用于計算frame@property (nonatomic, assign) NSInteger lineNum;? ? ? ?? ///< 列數(shù)

@property (nonatomic, assign) NSInteger eachLineWidth;? ? ? ? ? ? ? ? ? ? ? ///< 每列寬度,現(xiàn)平均,以后再擴(kuò)展

@property (nonatomic, assign) CGFloat horizontalSpace;? ? ? ? ? ? ? ? ? ? ? ///< 水平間距

@property (nonatomic, assign) CGFloat verticalSpace;? ? ? ? ? ? ? ? ? ? ? ? ///< 豎直間距

@property (nonatomic, assign) UIEdgeInsets edgeInset;? ? ? ? ? ? ? ? ? ? ?? ///< 邊距//所有frame

@property (nonatomic, strong) NSMutableArray*rectArray;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ///< 保存每個Frame值

@property (nonatomic, strong) NSMutableArray*eachLineLastRectArray;? //< 每列的最后一個rect

@property (nonatomic, strong) NSMutableArray*visibleAttributes;? ? ///< 可見Attributes

@end

//有四個必須改寫項:collectionViewContentSize、layoutAttributesForElementsInRect、layoutAttributesForItemAtIndexPath:、shouldInvalidateLayoutForBoundsChange

@implementation WaterFlowLayout

- (void)prepareLayout {

[super prepareLayout];

//水平間距

if (_delegate && [_delegate respondsToSelector:@selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:)]) {

_horizontalSpace = [_delegate collectionView:self.collectionView layout:self minimumLineSpacingForSectionAtIndex:0];

}

//豎直間距

if

(_delegate && [_delegate

respondsToSelector:@selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:)])

{

_verticalSpace = [_delegate collectionView:self.collectionView layout:self minimumInteritemSpacingForSectionAtIndex:0];

}

//邊距

if (_delegate && [_delegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)]) {

_edgeInset = [_delegate collectionView:self.collectionView layout:self insetForSectionAtIndex:0];

}

//列數(shù)

if (_delegate && [_delegate respondsToSelector:@selector(collectionView:numberOfLineForSection:)]) {

NSInteger lineNum = [_delegate collectionView:self.collectionView numberOfLineForSection:0];

_lineNum = lineNum;

}

//每列寬度

_eachLineWidth

= (self.collectionView.frame.size.width - _edgeInset.left -

_edgeInset.right - MAX(0, _lineNum - 1) * _verticalSpace)/_lineNum;

//初始化

self.rectArray = [NSMutableArray array];

self.eachLineLastRectArray = [NSMutableArray array];

//計算rects,并把所有item的frame存起來

NSInteger count = 0;

if (_delegate && [_delegate respondsToSelector:@selector(collectionView:numberOfItemsInSection:)]) {

count = [_delegate collectionView:self.collectionView numberOfItemsInSection:0];

}

for (NSInteger i = 0; i < count; i++) {

CGSize size = CGSizeZero;

if (_delegate && [_delegate respondsToSelector:@selector(collectionView:layout:sizeForItemAtIndexPath:)]) {

size

= [_delegate collectionView:self.collectionView layout:self

sizeForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];

}

[self caculateLowestRectAppendToRectArrayAndEachLineLastRectArray:size];

}

}

#pragma mark - ==========================四大需要重寫項=========================

- (CGSize)collectionViewContentSize {

CGRect highest = [self caculateHighestRect];

return CGSizeMake(self.collectionView.frame.size.width, CGRectGetMaxY(highest) + _edgeInset.bottom);

}

/**

*? 只加載rect內(nèi)部分Attributes,確保低內(nèi)存

*/

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

NSArray *visibleIndexPaths = [self indexPathsOfItemsInRect:rect];

self.visibleAttributes = [NSMutableArray array];

for (NSIndexPath *indexPath in visibleIndexPaths) {

[_visibleAttributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];

}

return _visibleAttributes;

}

/**

*? 從rectArray中取對應(yīng)path的rect賦值。

*/

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {

UICollectionViewLayoutAttributes *attributes =

[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

CGRect rect = [_rectArray[indexPath.item] CGRectValue];

attributes.frame = rect;

return attributes;

}

/**

*? 是否應(yīng)該刷新layout(理想狀態(tài)是豫加載上一屏和下一屏,這樣就可以避免頻繁刷新,加載過多會導(dǎo)致內(nèi)存過大,具體多遠(yuǎn)由preloadHeight控制)

*/

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {

//直接拿第一個和最后一個計算其實不精確,以后再改進(jìn)

CGFloat startY = CGRectGetMaxY([[_visibleAttributes firstObject] frame]);

CGFloat endY = CGRectGetMinY([[_visibleAttributes lastObject] frame]);

CGFloat offsetY = self.collectionView.contentOffset.y;

if (startY + preloadHeight >= offsetY ||

endY - preloadHeight <= offsetY + self.collectionView.frame.size.height) {

return YES;

}

return NO;

}

#pragma mark - ==================其它====================

//計算最低rect,并把最低rect添加進(jìn)rectArray和eachLineLastRectArray

- (void)caculateLowestRectAppendToRectArrayAndEachLineLastRectArray:(CGSize)newSize {

CGRect newRect;

if (_rectArray.count < _lineNum) {

newRect

= CGRectMake(_rectArray.count * (_eachLineWidth + _horizontalSpace) +

_edgeInset.left, _edgeInset.top, _eachLineWidth, newSize.height);

[_eachLineLastRectArray addObject:[NSValue valueWithCGRect:newRect]];

}

else {

CGRect lowestRect = [[_eachLineLastRectArray firstObject] CGRectValue];

NSInteger lowestIndex = 0;

for (NSInteger i = 0; i < _eachLineLastRectArray.count; i++) {

CGRect curruntRect = [_eachLineLastRectArray[i] CGRectValue];

if (CGRectGetMaxY(curruntRect) < CGRectGetMaxY(lowestRect)) {

lowestRect = curruntRect;

lowestIndex = i;

}

}

newRect = CGRectMake(lowestRect.origin.x, CGRectGetMaxY(lowestRect) + _verticalSpace, _eachLineWidth, newSize.height);

[_eachLineLastRectArray replaceObjectAtIndex:lowestIndex withObject:[NSValue valueWithCGRect:newRect]];

}

[_rectArray addObject:[NSValue valueWithCGRect:newRect]];

}

//計算最高rect,用來調(diào)整contentSize

- (CGRect)caculateHighestRect {

if (_rectArray.count < _lineNum) {

CGRect

newRect = CGRectMake(_rectArray.count * (_eachLineWidth +

_horizontalSpace) + _edgeInset.left, _edgeInset.top, _eachLineWidth, 0);

return newRect;

}

else {

CGRect highestRect = [_rectArray[_rectArray.count - _lineNum] CGRectValue];

for (NSInteger i = _rectArray.count - _lineNum; i < _rectArray.count; i++) {

CGRect curruntRect = [_rectArray[i] CGRectValue];

if (CGRectGetMaxY(curruntRect) > CGRectGetMaxY(highestRect)) {

highestRect = curruntRect;

}

}

return highestRect;

}

}

//當(dāng)前應(yīng)該顯示到屏幕上的items

- (NSArray *)indexPathsOfItemsInRect:(CGRect)rect {

CGFloat startY = self.collectionView.contentOffset.y;

CGFloat endY = startY + self.collectionView.frame.size.height;

NSMutableArray *items = [NSMutableArray array];

for (NSInteger i = 0; i < _rectArray.count; i++) {

CGRect rect = [_rectArray[i] CGRectValue];

if ((CGRectGetMaxY(rect) >= startY &&

CGRectGetMaxY(rect) <= endY ) ||

(CGRectGetMinY(rect) >= startY &&

CGRectGetMinY(rect) <= endY )) {

[items addObject:[NSIndexPath indexPathForItem:i inSection:0]];

}

}

return items;

}

@end

然后為了實現(xiàn)類似于淘寶商品詳情點擊圖片放大功能,再寫一個圖片放大視圖控制器

////? ToyDetailsBigImgaeViewController.m

//? WaterfallsFlowNetworkImage//

//? Created by CE on 2017/6/6.

//? Copyright ? 2017年 CE. All rights reserved.

//#import "ToyDetailsBigImgaeViewController.h"

#import "UIImageView+WebCache.h"

#import "ViewController.h"

@interface ToyDetailsBigImgaeViewController (){

UIScrollView *_scrollView;

}

@end

@implementation ToyDetailsBigImgaeViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self createScrollView];

self.navigationController.navigationBar.hidden = YES;

}

- (void)viewWillDisappear:(BOOL)animated{

self.navigationController.navigationBar.hidden = NO;

}

-(void)createScrollView{

_scrollView = [[UIScrollView alloc]initWithFrame:self.view.frame];

_scrollView.backgroundColor = [UIColor grayColor];

UIImageView *imageView = [[UIImageView alloc]initWithFrame:self.view.frame];

//UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_LH)];

[_scrollView addSubview:imageView];

imageView.contentMode = UIViewContentModeScaleAspectFit;

[imageView sd_setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@",self.url]]];

//尺寸

_scrollView.contentSize = imageView.frame.size;

//偏移量

_scrollView.contentOffset = CGPointMake(1000, 500);

//設(shè)置是否回彈

_scrollView.bounces = NO;

//設(shè)置邊距

//_scrollView.contentInset = UIEdgeInsetsMake(10, 10, 10, 10);

_scrollView.contentInset = UIEdgeInsetsMake(1, 1, 1, 1);

//設(shè)置是否可以滾動

_scrollView.scrollEnabled = YES;

//是否可以會到頂部

_scrollView.scrollsToTop = YES;

//按頁滾動

//scrollView.pagingEnabled = YES;

//設(shè)置滾動條

_scrollView.showsHorizontalScrollIndicator = YES;

_scrollView.showsVerticalScrollIndicator = NO;

//設(shè)置滾動條的樣式

_scrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite;

imageView.userInteractionEnabled = YES;

//代理

_scrollView.delegate = self;

//CGFloat imageWidth = imageView.frame.size.width;

//設(shè)置最小和最大縮放比例

//_scrollView.minimumZoomScale = SCREEN_W/imageWidth;

//_scrollView.maximumZoomScale = 1.5;

_scrollView.minimumZoomScale = 0.2;

//_scrollView.maximumZoomScale = 2.0;

_scrollView.maximumZoomScale = imageView.frame.size.width * 3 / self.view.frame.size.width;

[self.view addSubview:_scrollView];

//給imageView添加手勢

//創(chuàng)建單擊雙擊手勢

UITapGestureRecognizer *oneTgr = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapClick:)];

//oneTgr.numberOfTapsRequired = 1;

[imageView addGestureRecognizer:oneTgr];

UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapClick:)];

tgr.numberOfTapsRequired = 2;

[imageView addGestureRecognizer:tgr];

[oneTgr requireGestureRecognizerToFail:tgr];

}

-(void)tapClick:(UITapGestureRecognizer *)tap{

if (tap.numberOfTapsRequired == 1) {

printf("單擊手勢識別成功\n");

[self.navigationController popViewControllerAnimated:NO];

} else {

printf("雙擊手勢識別成功\n");

//zoomScale當(dāng)前的縮放比例

if (_scrollView.zoomScale == 1.0) {

[_scrollView setZoomScale:_scrollView.maximumZoomScale animated:YES];

} else {

[_scrollView setZoomScale:1.0 animated:YES];

}

}

}

#pragma mark - 代理

//- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

//

//? ? NSLog(@"滾動");

//

//}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale {

if (scale <= 0.5) {

//當(dāng)縮放比例小于0.5時返回上一級

[self.navigationController popViewControllerAnimated:NO];

}

}

//只要縮放就會調(diào)用此方法

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {

NSLog(@"發(fā)生縮放");

}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{

NSLog(@"將要開始拖動");

}

-

(void)scrollViewWillEndDragging:(UIScrollView *)scrollView

withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint

*)targetContentOffset {

NSLog(@"將要結(jié)束拖動");

}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

NSLog(@"拖動結(jié)束");

}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{

NSLog(@"將要開始減速");

}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

NSLog(@"已經(jīng)結(jié)束減速");//停止?jié)L動

}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{

NSLog(@"滾動動畫結(jié)束");

}

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{

NSLog(@"正在縮放");

//放回對那個子視圖進(jìn)行縮放? 前提是有縮放比例

return scrollView.subviews[0];

}

- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view{

NSLog(@"縮放開始");

}

//- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale{

//? ? NSLog(@"%@",view);

//

//

//#if 0

//

//? ? //放大時會出現(xiàn)問題

//? ? if (scale <1.0) {

//? ? ? ? CGPoint center = view.center;

//? ? ? ? center.y = HEIGHT/2-64;

//? ? ? ? view.center = center;

//? ? }

//

//#endif

//

//

//? ? if (view.frame.size.width > SCREEN_W) {

//? ? ? ? scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);

//

//? ? } else {

//

//? ? ? ? //距邊框的距離

//? ? ? ? [UIView animateWithDuration:0.5 animations:^{

//? ? ? ? ? ? scrollView.contentInset = UIEdgeInsetsMake((SCREEN_LH-view.frame.size.width)/2, 0, 0, 0 );

//

//? ? ? ? }];

//? ? }

//

//? ? NSLog(@"縮放結(jié)束");

//}

//是否可以滾動到頂部 前提是前面scrollToTop = YES;

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView{

return YES;

}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView{

NSLog(@"已經(jīng)滾動到頂部");

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

/*

#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

// Get the new view controller using [segue destinationViewController].

// Pass the selected object to the new view controller.

}

*/

@end

在這里處理主要的業(yè)務(wù)邏輯。這里之前有一個BUG,當(dāng)圖片顯示高度超過屏幕高度時,會閃一下然后不顯示,單純的從cell高度方面找解決方案不太容易,最快的就是直接在self.view

上放一個scrollView ,

然后把collectionView放在scrollView上,依靠scrollView的滾動代替collectionView滑動。這樣需要動態(tài)地計算scrollView的contentSize來實現(xiàn)如原生collectionView滑動展示效果。當(dāng)然解決這個BUG的方法很多,還有很多更簡單的。可以留言交流。

////? ViewController.m

//? WaterfallsFlowNetworkImage//

//? Created by CE on 2017/6/6.

//? Copyright ? 2017年 CE. All rights reserved.

//#import "ViewController.h"

#import "WaterFlowLayout.h"

#import "UIView+MZwebCache.h"

#import "AFNetworking.h"

#import "MJRefresh.h"

#import "UIImageView+WebCache.h"

#import "UIButton+WebCache.h"

#import "SDWebImageManager.h"

#import "SDWebImageDownloader.h"

#import "UIImage+GIF.h"

#import "NSData+ImageContentType.h"

#import "ToyDetailsBigImgaeViewController.h"

@interface ViewController (){

NSInteger lines;

//cell高度

CGFloat cellCurrentHight;

//最大圖片高度

CGFloat imageMAXHight;

}

@property (nonatomic, strong) NSMutableArray *dataArray;

@property (nonatomic, strong) UICollectionView *collectionView;

@property (nonatomic,strong) NSArray *imagArray;

@property (nonatomic,strong) UIScrollView *backgroundScrollView;

@property (nonatomic,strong) NSMutableDictionary *MDic;

@end

@implementation ViewController

//屏幕尺寸

#define SCREEN_H [UIScreen mainScreen].bounds.size.height

#define SCREEN_W [UIScreen mainScreen].bounds.size.width

- (void)viewDidLoad {

[super viewDidLoad];

self.MDic = [[NSMutableDictionary alloc] init];

[self createUI];

}

- (void)createUI{

self.backgroundScrollView = [[UIScrollView alloc]initWithFrame:self.view.bounds];

[self.view addSubview:self.backgroundScrollView];

self.backgroundScrollView.scrollEnabled = YES;

self.backgroundScrollView.contentSize = CGSizeMake(SCREEN_W, SCREEN_H * 1.2);

self.backgroundScrollView.backgroundColor = [UIColor whiteColor];

//是否回彈

//self.backgroundScrollView.bounces = NO;

self.backgroundScrollView.alwaysBounceVertical = YES;

//self.backgroundScrollView.showsHorizontalScrollIndicator = NO;

//self.backgroundScrollView.showsVerticalScrollIndicator = NO;

WaterFlowLayout *flowOut = [[WaterFlowLayout alloc] init];

flowOut.delegate = self;

self.collectionView =

[[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_W, SCREEN_H * 1.2)

collectionViewLayout:flowOut];

_collectionView.delegate = self;

_collectionView.dataSource = self;

_collectionView.alwaysBounceVertical = YES;

_collectionView.scrollEnabled = NO;

_collectionView.backgroundColor = [UIColor colorWithWhite:0.9 alpha:0.8];

[self.backgroundScrollView addSubview:_collectionView];

[_collectionView registerNib:[UINib nibWithNibName:@"MainCell" bundle:nil]

forCellWithReuseIdentifier:@"MainCell"];

//默認(rèn)列數(shù)

lines = 1;

self.title = [NSString stringWithFormat:@"%ld列",lines];

UISegmentedControl *segment = [[UISegmentedControl alloc]

initWithItems:@[ @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8" ]];

segment.frame = CGRectMake(0, SCREEN_H - 40, SCREEN_W, 40);

segment.selectedSegmentIndex = 0;

[self.view addSubview:segment];

[segment addTarget:self

action:@selector(changeLines:)

forControlEvents:UIControlEventValueChanged];

//加載數(shù)據(jù)

[self prepareData];

}

- (void)prepareData {

_imagArray = @[? ? ? ? //圖片鏈接

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/QVQTlkgfGtvY8Ml1e5*C.0.r2rvYkiNmkuEgOxChKdE!/r/dIIBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/d6DS.ut7JDKCngxXd0CaTDVjzkZCCjDfPQgRVThM9vE!/r/dG0BAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/*TawSnqTpDyDN9StiXJlG6naEToM6KLa0XoRAFgOxi4!/r/dGwBAAAAAAAA",

@"http://a3.qpic.cn/psb?/V14FKYxo0UhIAP/py.OcSKU4wVb4vXlqxv.DKIY.XEkzx7U.n838lTPfak!/b/dN0AAAAAAAAA&bo=gAJTGwAAAAAFB.4!&rf=viewer_4",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/Vjr2oZ*N4wty.iWKnF4TGfqh7SBFusq2bYZ7pzgISNQ!/r/dGwBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/tSDFJivi0z0vnoXdiEdkYUr6pnwmedJYdt*Y2QgXBg8!/r/dG4BAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/gCOv8dKdS0v21xG9MX2UngH655hg5AsuWyIu*0u5WZk!/r/dGwBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/x2TP3LgwjRjrLWhK*TwGOUvfB9Ipyv8pXS10FQPJRQY!/r/dGwBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/wCAh6JN5RffRMbIabosoKoOqEFz8RP7FuFZl2vMVwkI!/r/dG0BAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/1M7RPK9zA5EWUIkzf01qfx*Q*fdlGcq7jAFZqC40m5g!/r/dG0BAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/XnxwggzMNrYrhLWdEMSfCazNiJuO8nDysOyZ0Qx3DhQ!/r/dGwBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/TMJrjlo3D*oMYXrpLJmDNyfrW0dKnzPZF2DMSW8Y.Ek!/r/dIMBAAAAAAAA",

@"http://r.photo.store.qq.com/psb?/V14FKYxo4VCpyi/DK7tRNLecsbzH9FB7hT1pzrlQnz6vfKsCrg3GqE5qRA!/r/dIQBAAAAAAAA",];

self.dataArray = [NSMutableArray array];

for (NSInteger i = 0; i < _imagArray.count; i++) {

MainModel *model = [[MainModel alloc] init];

model.imageUrl = _imagArray[i % _imagArray.count];

[_dataArray addObject:model];

}

}

//更改列數(shù)

- (void)changeLines:(UISegmentedControl *)segment {

lines = segment.selectedSegmentIndex + 1;

[_collectionView reloadData];

self.title = [NSString stringWithFormat:@"%ld列",lines];

}

#pragma mark - UICollectionView DataSource Methods

- (NSInteger)collectionView:(UICollectionView *)collectionView

numberOfItemsInSection:(NSInteger)section {

return _dataArray.count;

}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView

cellForItemAtIndexPath:(NSIndexPath *)indexPath {

__weak typeof(self) weakSelf = self;

MainCell *cell = (MainCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"MainCell" forIndexPath:indexPath];

cell.indexPath = indexPath;

cell.model = _dataArray[indexPath.row];

cell.sizeChanged = ^() {

//這里每次加載完圖片后,得到圖片的比例會再次調(diào)用刷新此item,重新計算位置,會導(dǎo)致效率低。最優(yōu)做法是服務(wù)器返回圖片寬高比例;其次把加載完成后的寬高數(shù)據(jù)也緩存起來。

[weakSelf.collectionView reloadItemsAtIndexPaths:@[indexPath]];

};

return cell;

}

#pragma mark - UICollectionView Delegate Methods

- (CGFloat)collectionView:(UICollectionView *)collectionView

layout:(UICollectionViewLayout *)collectionViewLayout

minimumLineSpacingForSectionAtIndex:(NSInteger)section {

return 5;

}

- (CGFloat)collectionView:(UICollectionView *)collectionView

layout:(UICollectionViewLayout *)collectionViewLayout

minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {

return 5;

}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView

layout:(UICollectionViewLayout *)collectionViewLayout

insetForSectionAtIndex:(NSInteger)section {

return UIEdgeInsetsMake(10, 10, 10, 10);

}

//返回每個小方塊寬高,但由于是在WaterFlowLayout處理,只取了高,寬是由列數(shù)平均分

- (CGSize)collectionView:(UICollectionView *)collectionView

layout:(UICollectionViewLayout *)collectionViewLayout

sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

MainModel *model = _dataArray[indexPath.row];

NSInteger lineNum = [self collectionView:_collectionView numberOfLineForSection:0];

CGFloat width = ((SCREEN_W - 10) - (lineNum - 1) * 5) / lineNum;

if (model.imageSize.width > 0) {

CGSize imageSize = model.imageSize;

//獲取每個cell的高度 存入字典

CGFloat HHH = width / imageSize.width * imageSize.height;

NSLog(@"HHH = %f",width / imageSize.width * imageSize.height);

NSLog(@"indexPath.row = %ld",indexPath.row);

NSNumber *cellHight = [NSNumber numberWithFloat:HHH];

NSString *indexPathRow = [NSString stringWithFormat:@"%ld",indexPath.row];

NSLog(@"indexPathRow = %@",indexPathRow);

[self.MDic setValue:cellHight forKey:indexPathRow];

NSLog(@"self.MDic = %@",self.MDic);

NSArray *otherCellHightArray = [self.MDic allValues];

cellCurrentHight = 0;

for (NSNumber *cellHightNumber? in otherCellHightArray) {

CGFloat cellHightFloat = [cellHightNumber floatValue];

cellCurrentHight += cellHightFloat;

if (indexPath.row == 0) {

imageMAXHight = cellHightFloat;

}

if (imageMAXHight < cellHightFloat) {

imageMAXHight = cellHightFloat;

}

}

cellCurrentHight = cellCurrentHight / lines;

if (imageMAXHight > cellCurrentHight) {

cellCurrentHight = imageMAXHight;

}

NSLog(@"cellCurrentHight = %f",cellCurrentHight);

//賦值

self.backgroundScrollView.contentSize = CGSizeMake(SCREEN_W, cellCurrentHight + 64 + 40 + 40);

_collectionView.frame = CGRectMake(0, 0, SCREEN_W, cellCurrentHight + 64 + 40 + 40);

NSLog(@"cellCurrentHight = %f",cellCurrentHight);

return CGSizeMake(width, width / imageSize.width * imageSize.height);

}

return CGSizeMake(width, 300);

}

- (void)collectionView:(UICollectionView *)collectionView

didSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath {

NSLog(@"點擊了第%ld個", indexPath.row);

ToyDetailsBigImgaeViewController *toyDetailsBigImgaeVC = [[ToyDetailsBigImgaeViewController alloc] init];

NSString *url = _imagArray[indexPath.row];

toyDetailsBigImgaeVC.url = url;

[self.navigationController pushViewController:toyDetailsBigImgaeVC animated:NO];

}

#pragma mark - WaterFlowout代理,請?zhí)钊敕祷囟嗌倭?/p>

- (NSInteger)collectionView:(UICollectionView *)collectionView

numberOfLineForSection:(NSInteger)section {

return lines;

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

@end

@implementation MainCell

- (void)setModel:(MainModel *)model {

_model = model;

__weak typeof(self) weakSelf = self;

[_mainImgv

setImageWithUrl:[NSURL URLWithString:model.imageUrl]

placeHolder:[UIImage imageNamed:@"loading.jpg"] completion:^(UIImage

*image, BOOL bFromCache, NSError *error) {

if (!error && image) {

if (model.imageSize.width < 0.0001) {

model.imageSize = image.size;

if (weakSelf.sizeChanged) {

weakSelf.sizeChanged();

}

}

}

}];

}

- (void)dealloc {

NSLog(@"=======%@ =%@ deallloc",self ,[self class]);

}

@end

@implementation MainModel

@end

到此為止基本所有的代碼都貼出來,這個詳情頁很容易實現(xiàn)。代碼已經(jīng)上傳到GitHub,可以直接下載瀏覽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。