学习带图片的列表

官方 LazyTableImages demo  http://download.csdn.net/detail/jlyidianyuan/5726749

分析源码是学习的好方法。

源码结构如上,不能运行,加红框内容。

项目结构

挨个看源文件

/*
Copyright (C) 2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information Abstract:
Application delegate for the LazyTableImages sample.
It also downloads in the background the "Top Paid iPhone Apps" RSS feed using NSURLSession/NSURLSessionDataTask.
*/ #import "LazyTableAppDelegate.h"
#import "RootViewController.h"
#import "ParseOperation.h"
#import "AppRecord.h" // the http URL used for fetching the top iOS paid apps on the App Store
static NSString *const TopPaidAppsFeed =
@"http://phobos.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/limit=75/xml"; @interface LazyTableAppDelegate () // the queue to run our "ParseOperation" NSOperationQueue解析队q列,类似java线程池
@property (nonatomic, strong) NSOperationQueue *queue; // the NSOperation driving the parsing of the RSS feed 解析类操作
@property (nonatomic, strong) ParseOperation *parser; @end #pragma mark - @implementation LazyTableAppDelegate // The app delegate must implement the window @property
// from UIApplicationDelegate @protocol to use a main storyboard file.
//
@synthesize window; // -------------------------------------------------------------------------------
// application:didFinishLaunchingWithOptions:
// -------------------------------------------------------------------------------
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//实例化联网请求
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:TopPaidAppsFeed]]; // create an session data task to obtain and the XML feed
// 使用9.0以后的联网操作类,之前可能使用NSURLConnection
NSURLSessionDataTask *sessionTask =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// 获取联网状态代码 200 ,300,400,500等
// in case we want to know the response status code
//NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode]; if (error != nil)//如果有错误
{
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO; if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
{
// if you get error NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022),
// then your Info.plist has not been properly configured to match the target server.
//错误码含意查询:https://www.meiwen.com.cn/subject/jjjdnttx.html
//在工程的 info.plist 文件中添加 https允许
abort();
}
else
{
//其它错误交给handleError处理
[self handleError:error];
}
}];
}
else
{
//没错误往下走
// create the queue to run our ParseOperation 初始化解析队列
self.queue = [[NSOperationQueue alloc] init]; // create an ParseOperation (NSOperation subclass) to parse the RSS feed data so that the UI is not blocked 初始化解析操作类
_parser = [[ParseOperation alloc] initWithData:data];
//__weak 弱引用,这里使用弱引用,防止线程引用对象造成内存泄漏。要回收LazyTableAppDelegate?真如此,程序已经结束。没必要了。
__weak LazyTableAppDelegate *weakSelf = self;
//添加解析错误时回调的block ,block 类似java interface 或者理解为内部类。好比java OnClickLister.
self.parser.errorHandler = ^(NSError *parseError) {
//dispatch_async GCD 方式在主线程上操作的方法。相关学习:线程如何操作主线程的3种方法.
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//扔给本类的handleError方法处理。
[weakSelf handleError:parseError];
});
}; // referencing parser from within its completionBlock would create a retain cycle
__weak ParseOperation *weakParser = self.parser; //解析完成返回处理
self.parser.completionBlock = ^(void) {
// The completion block may execute on any thread. Because operations
// involving the UI are about to be performed, make sure they execute on the main thread.
//结果需要在主线程更新
dispatch_async(dispatch_get_main_queue(), ^{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (weakParser.appRecordList != nil)
{
//如果返回的解析集合不为空
RootViewController *rootViewController =
(RootViewController *)[(UINavigationController *)weakSelf.window.rootViewController topViewController];
//RootViewController : UITableViewController 解析数据给到tableview
rootViewController.entries = weakParser.appRecordList; // tell our table view to reload its data, now that parsing has completed 更新tableview
[rootViewController.tableView reloadData];
}
}); // we are finished with the queue and our ParseOperation
weakSelf.queue = nil;
}; [self.queue addOperation:self.parser]; // this will start the "ParseOperation"
}
}]; [sessionTask resume]; // show in the status bar that network activity is starting
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES; return YES;
} // -------------------------------------------------------------------------------
// handleError:error
// Reports any error with an alert which was received from connection or loading failures.
// -------------------------------------------------------------------------------
- (void)handleError:(NSError *)error
{
NSString *errorMessage = [error localizedDescription]; // alert user that our current record was deleted, and then we leave this view controller
//
UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Cannot Show Top Paid Apps", @"")
message:errorMessage
preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *OKAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// dissmissal of alert completed
}]; [alert addAction:OKAction];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
} @end
/*
Copyright (C) 2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information Abstract:
Controller for the main table view of the LazyTable sample.
This table view controller works off the AppDelege's data model.
produce a three-stage lazy load:
1. No data (i.e. an empty table)
2. Text-only data from the model's RSS feed
3. Images loaded over the network asynchronously This process allows for asynchronous loading of the table to keep the UI responsive.
Stage 3 is managed by the AppRecord corresponding to each row/cell. Images are scaled to the desired height.
If rapid scrolling is in progress, downloads do not begin until scrolling has ended.
*/ #import "RootViewController.h"
#import "AppRecord.h"
#import "IconDownloader.h" #define kCustomRowCount 7 static NSString *CellIdentifier = @"LazyTableCell";
static NSString *PlaceholderCellIdentifier = @"PlaceholderCell"; #pragma mark - @interface RootViewController () <UIScrollViewDelegate> // the set of IconDownloader objects for each app
@property (nonatomic, strong) NSMutableDictionary *imageDownloadsInProgress; @end #pragma mark - @implementation RootViewController // -------------------------------------------------------------------------------
// viewDidLoad
// -------------------------------------------------------------------------------
- (void)viewDidLoad
{
[super viewDidLoad]; _imageDownloadsInProgress = [NSMutableDictionary dictionary];
} // -------------------------------------------------------------------------------
// terminateAllDownloads
// -------------------------------------------------------------------------------
- (void)terminateAllDownloads
{
//停止下载
// terminate all pending download connections
NSArray *allDownloads = [self.imageDownloadsInProgress allValues];
//数组的makeObjectsPerformSelector:SEL方法来减少自己写循环代码.让数组中每个对象都执行 cancelDownload 方法
[allDownloads makeObjectsPerformSelector:@selector(cancelDownload)];
//从字典移除记录。
[self.imageDownloadsInProgress removeAllObjects];
} // -------------------------------------------------------------------------------
// dealloc
// If this view controller is going away, we need to cancel all outstanding downloads.
// -------------------------------------------------------------------------------
- (void)dealloc
{
// terminate all pending download connections
[self terminateAllDownloads];
} // -------------------------------------------------------------------------------
// didReceiveMemoryWarning
// -------------------------------------------------------------------------------
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning]; // terminate all pending download connections
[self terminateAllDownloads];
} #pragma mark - UITableViewDataSource // -------------------------------------------------------------------------------
// tableView:numberOfRowsInSection:
// Customize the number of rows in the table view.
// -------------------------------------------------------------------------------
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSUInteger count = self.entries.count; // if there's no data yet, return enough rows to fill the screen
if (count == )
{
return kCustomRowCount;
}
return count;
} // -------------------------------------------------------------------------------
// tableView:cellForRowAtIndexPath:
// -------------------------------------------------------------------------------
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = nil; NSUInteger nodeCount = self.entries.count; if (nodeCount == && indexPath.row == )
{
// add a placeholder cell while waiting on table data 在storyboard中定义的加载中...
cell = [tableView dequeueReusableCellWithIdentifier:PlaceholderCellIdentifier forIndexPath:indexPath];
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // Leave cells empty if there's no data yet
if (nodeCount > )
{
// Set up the cell representing the app
AppRecord *appRecord = (self.entries)[indexPath.row]; cell.textLabel.text = appRecord.appName;
cell.detailTextLabel.text = appRecord.artist; // Only load cached images; defer new downloads until scrolling ends
//这里注释得很明白,只加载cached(已缓存的)图片。
//【defer】 英[dɪˈfɜː(r)] ,美[dɪˈfɜːr] ,v.推迟; 延缓; 展期;
if (!appRecord.appIcon)//如果为空
{
if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
{
//如果没有拖动,也没在惯性滑动。开始下载。
[self startIconDownload:appRecord forIndexPath:indexPath];
}
// if a download is deferred or in progress, return a placeholder image
//如果正在下载中给一个默认图。
cell.imageView.image = [UIImage imageNamed:@"Placeholder.png"];
}
else
{
// (self.entries)[indexPath.row]; 直接使用
cell.imageView.image = appRecord.appIcon;
}
}
} return cell;
} #pragma mark - Table cell image support // -------------------------------------------------------------------------------
// startIconDownload:forIndexPath: 第N个Cell的下载图片资源
// -------------------------------------------------------------------------------
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = (self.imageDownloadsInProgress)[indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
iconDownloader.appRecord = appRecord;
[iconDownloader setCompletionHandler:^{ //图片加载完成时回调本段代码 UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; // Display the newly loaded image
cell.imageView.image = appRecord.appIcon; // Remove the IconDownloader from the in progress list.
// This will result in it being deallocated. 从字典中把下载对象移除。之前是有记录要下载的。
[self.imageDownloadsInProgress removeObjectForKey:indexPath]; }];
//在开始下载后,把下载对象记录到字典管理。
(self.imageDownloadsInProgress)[indexPath] = iconDownloader;
[iconDownloader startDownload];
}
} // -------------------------------------------------------------------------------
// loadImagesForOnscreenRows
// This method is used in case the user scrolled into a set of cells that don't
// have their app icons yet.
// -------------------------------------------------------------------------------
- (void)loadImagesForOnscreenRows
{
if (self.entries.count > )
{
//获取tableview 当前可见的编号。indexPathsForVisibleRows
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths)
{
AppRecord *appRecord = (self.entries)[indexPath.row]; if (!appRecord.appIcon)
// Avoid the app icon download if the app already has an icon
// 如果没有图片 开始下载图片
{
[self startIconDownload:appRecord forIndexPath:indexPath];
}
}
}
} #pragma mark - UIScrollViewDelegate // -------------------------------------------------------------------------------
// scrollViewDidEndDragging:willDecelerate:
// Load images for all onscreen rows when scrolling is finished.
// tableview 滚动结束事件回调。停下来时,加载当前屏的图片。
// -------------------------------------------------------------------------------
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if (!decelerate)
{
[self loadImagesForOnscreenRows];
}
} // -------------------------------------------------------------------------------
// scrollViewDidEndDecelerating:scrollView
// When scrolling stops, proceed to load the app icons that are on screen.
// -------------------------------------------------------------------------------
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
//惯性滑动结束时,开始加载图片,同上。
[self loadImagesForOnscreenRows];
} @end
/*
Copyright (C) 2017 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information Abstract:
Helper object for managing the downloading of a particular app's icon.
It uses NSURLSession/NSURLSessionDataTask to download the app's icon in the background if it does not
yet exist and works in conjunction with the RootViewController to manage which apps need their icon.
*/ #import "IconDownloader.h"
#import "AppRecord.h" #define kAppIconSize 48 @interface IconDownloader () @property (nonatomic, strong) NSURLSessionDataTask *sessionTask; @end #pragma mark - @implementation IconDownloader // -------------------------------------------------------------------------------
// startDownload
// -------------------------------------------------------------------------------
- (void)startDownload
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.appRecord.imageURLString]]; // create an session data task to obtain and download the app icon
_sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// 这里使用NSURLSessionDataTask 进行数据加载。@NSURLSession @NSURLSessionDataTask 属一个知识体系,可以系统学习。
//_sessionTask 声明为了属性,目的是可以进行控制。 // in case we want to know the response status code
//NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode]; if (error != nil)
{
if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
{
// if you get error NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022),
// then your Info.plist has not been properly configured to match the target server.
//
abort();
}
} //以上代码好像是规范格式。 [[NSOperationQueue mainQueue] addOperationWithBlock: ^{ // Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:data]; if (image.size.width != kAppIconSize || image.size.height != kAppIconSize)
{
//对图片进行裁剪操作。
CGSize itemSize = CGSizeMake(kAppIconSize, kAppIconSize);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0f);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
self.appRecord.appIcon = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
self.appRecord.appIcon = image;
} //不如何image给到了appRecord数据模型。 // call our completion handler to tell our client that our icon is ready for display
//通知加载完成
if (self.completionHandler != nil)
{
self.completionHandler();
}
}];
}]; //开启task
[self.sessionTask resume];
} // -------------------------------------------------------------------------------
// cancelDownload
// -------------------------------------------------------------------------------
- (void)cancelDownload
{
[self.sessionTask cancel];
_sessionTask = nil;
} @end

代码比较简单

1.appdelegate 去下载数据并解析。并更新tableview

//RootViewController : UITableViewController 解析数据给到tableview

rootViewController.entries = weakParser.appRecordList;

2.使用 model给tableview展示。

@property (nonatomic, strong) NSString *appName;

@property (nonatomic, strong) UIImage *appIcon;

@property (nonatomic, strong) NSString *artist;

@property (nonatomic, strong) NSString *imageURLString;

@property (nonatomic, strong) NSString *appURLString;

appIcon开始时为空,当展示在屏幕时,起线程去加载。并记录在

self.imageDownloadsInProgress

第一个下载对应一个线程对象。

3.这个代码  UIImage 会不数增加,数量够多,内存肯定溢出。

所以需要图片的加载策略。图片的三级缓存可系统独立学习。

【iOS入门】UITableView加载图片的更多相关文章

  1. iOS - UITableView加载网络图片 cell适应图片高度

    使用xib创建自定制cell   显示图片   创建一个继承UITableViewCell的类   勾选xib 如下是xib创建图 xib 向.h拖拽一个关联线 .h .m 2.代码创建(使用三方适配 ...

  2. iOS NSOperation 异步加载图片 封装NSOperation 代理更新

    #import <Foundation/Foundation.h> @class MYOperation; @protocol MYOperationDelecate <NSObje ...

  3. iOS之UITableView加载网络图片cell自适应高度

    #pragma mark- UITableView - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSI ...

  4. iOS 14 YYAnimatedImageView加载图片失败处理

    1.问题出在YYAnimatedImageView源码一个方法中 - (void)displayLayer:(CALayer *)layer { if (_curFrame) { layer.cont ...

  5. IOS UITableView 加载未知宽高图片的解决方案

    在开发中遇到了UITableView列表 UITableViewCell装载图片但不知Image的宽高 问题. 在解决该问题的时候,首先想到的是异步加载图片 采用第三方框架SDWebImage 实现对 ...

  6. ios UITableView 异步加载图片并防止错位

    UITableView 重用 UITableViewCell 并异步加载图片时会出现图片错乱的情况 对错位原因不明白的同学请参考我的另外一篇随笔:http://www.cnblogs.com/lesl ...

  7. IOS中UITableView异步加载图片的实现

    本文转载至 http://blog.csdn.net/enuola/article/details/8639404  最近做一个项目,需要用到UITableView异步加载图片的例子,看到网上有一个E ...

  8. iOS网络加载图片缓存与SDWebImage

    加载网络图片可以说是网络应用中必备的.如果单纯的去下载图片,而不去做多线程.缓存等技术去优化,加载图片时的效果与用户体验就会很差. 一.自己实现加载图片的方法 tips: *iOS中所有网络访问都是异 ...

  9. iOS: imageIO完成渐进加载图片

    imageIO完成渐进加载图片 不得不说,人都是有惰性的,一个月又快结束了,这个月虽说有点儿忙,但是绝对不差写几篇博客的时间,有时间去n次桌球厅,有时间玩n把英雄联盟,所谓小撸怡情大撸伤身,这个月游戏 ...

随机推荐

  1. BurpSuite详解

    转载自:http://www.nxadmin.com/tools/689.html 本文由阿德马翻译自国外网站,请尊重劳动成果,转载注明出处 Burp Suite是Web应用程序测试的最佳工具之一,其 ...

  2. UVA - 11105 Semi-prime H-numbers(H-半素数)

    题意:所有形如4n+1(n为非负整数)的数叫H数.定义1是唯一的单位H数,H素数是指本身不是1,且不能写成两个不是1的H数的乘积.H-半素数是指能写成两个H素数的乘积的H数(这两个数可以相同也可以不同 ...

  3. POJ 2632:Crashing Robots

    Crashing Robots Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 8424   Accepted: 3648 D ...

  4. 6 ~ express ~ 搭建用户注册前端页面

    一,前端页面 /views/login.html <!DOCTYPE html> <html lang="en"> <head> <met ...

  5. filter滤镜兼容ie的rgba属性

    要在一个页面中设置一个半透明的白色div.这个貌似不是难题,只需要给这个div设置如下的属性即可: background: rgba(255,255,255,0.1); 但是要兼容到ie8.这个就有点 ...

  6. 六、CI框架之分配变量

    一.在controllers里面添加 $this->load->vars('m_Str1','我是一个字符串变量'); 二.在View中添加相应代码 界面显示效果如下: 不忘初心,如果您认 ...

  7. MarkDown简易教程+语法

    一.标题 一个#是一级,两个#是二级,最大六级 二.字体 1.加粗 要加粗的文字左右分别用两个*号包起来 2.斜体 要倾斜的文字左右分别用一个*号包起来 3.斜体加粗 要倾斜和加粗的文字左右分别用三个 ...

  8. springboot~不用模板执行html文件

    放到在resources/static目录下,创建目录或html文件,均可.如:

  9. Redis高级用法

    第一章 redis初识 1.1 Redis是什么 介绍 开源:早起版本2w3千行 基于键值对的存储系统:字典形式 多种数据结构:字符串,hash,列表,集合,有序集合 高性能,功能丰富 那些公司在用 ...

  10. Tensorflow——用openpose进行人体骨骼检测

    https://blog.csdn.net/eereere/article/details/80176007 参考资料code:https://github.com/ildoonet/tf-pose- ...