【iOS入门】UITableView加载图片
学习带图片的列表
官方 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加载图片的更多相关文章
- iOS - UITableView加载网络图片 cell适应图片高度
使用xib创建自定制cell 显示图片 创建一个继承UITableViewCell的类 勾选xib 如下是xib创建图 xib 向.h拖拽一个关联线 .h .m 2.代码创建(使用三方适配 ...
- iOS NSOperation 异步加载图片 封装NSOperation 代理更新
#import <Foundation/Foundation.h> @class MYOperation; @protocol MYOperationDelecate <NSObje ...
- iOS之UITableView加载网络图片cell自适应高度
#pragma mark- UITableView - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSI ...
- iOS 14 YYAnimatedImageView加载图片失败处理
1.问题出在YYAnimatedImageView源码一个方法中 - (void)displayLayer:(CALayer *)layer { if (_curFrame) { layer.cont ...
- IOS UITableView 加载未知宽高图片的解决方案
在开发中遇到了UITableView列表 UITableViewCell装载图片但不知Image的宽高 问题. 在解决该问题的时候,首先想到的是异步加载图片 采用第三方框架SDWebImage 实现对 ...
- ios UITableView 异步加载图片并防止错位
UITableView 重用 UITableViewCell 并异步加载图片时会出现图片错乱的情况 对错位原因不明白的同学请参考我的另外一篇随笔:http://www.cnblogs.com/lesl ...
- IOS中UITableView异步加载图片的实现
本文转载至 http://blog.csdn.net/enuola/article/details/8639404 最近做一个项目,需要用到UITableView异步加载图片的例子,看到网上有一个E ...
- iOS网络加载图片缓存与SDWebImage
加载网络图片可以说是网络应用中必备的.如果单纯的去下载图片,而不去做多线程.缓存等技术去优化,加载图片时的效果与用户体验就会很差. 一.自己实现加载图片的方法 tips: *iOS中所有网络访问都是异 ...
- iOS: imageIO完成渐进加载图片
imageIO完成渐进加载图片 不得不说,人都是有惰性的,一个月又快结束了,这个月虽说有点儿忙,但是绝对不差写几篇博客的时间,有时间去n次桌球厅,有时间玩n把英雄联盟,所谓小撸怡情大撸伤身,这个月游戏 ...
随机推荐
- PHP - 接收检验验证码 , 以JSON的形式将验证结果返回给前端
<?php session_start(); $codeT = strtoupper(trim($_POST['codeT']));//接收前端传来的数据,转换成大写 $raw ...
- 基于 burpsuite的web逻辑漏洞插件开发(来自JSRC安全小课堂,柏山师傅)
基于 burpsuite的web逻辑漏洞插件开发 BurpSuite 提供了插件开发接口,支持Java.Python.Ruby语言的扩展.虽然 BApp Store 上面已经提供了很多插件,其中也不乏 ...
- 路飞学城—Python爬虫实战密训班 第三章
路飞学城—Python爬虫实战密训班 第三章 一.scrapy-redis插件实现简单分布式爬虫 scrapy-redis插件用于将scrapy和redis结合实现简单分布式爬虫: - 定义调度器 - ...
- expdp远程导出oracle库
1.手动在本地建目录 E:\lvchengData 2.执行命令 create or replace directory data as 'E:\lvchengData\'; 3.为本地system用 ...
- lambda的题
def num(): return [lambda x: i*x for i in range(4)] print([m(2) for m in num()]) 这个式子,lambda相当于闭包函数, ...
- 79.常用的返回QuerySet对象的方法使用详解: filter, exclude,annotate
返回新的QuerySet的常用方法: 1.filter: 将满足条件的数据提取出来,返回一个新的QuerySet 以下所使用的模型article,category,定义模型models.py文件中,示 ...
- .NET 软件下面win10自动启动配置
1.设置所有用户登录都能启动,打开文件夹 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp 2.给要启动的应用程序创建快捷方式, ...
- JAVA初学者——Hello,World!
大家好,我是浩宇大熊猫 我本科专业学的是GIS(Geographical Information System),大学期间也学习了很多的编程语言,有C/C++/JAVA等 之前给我们授课的是韩冰老师, ...
- 十五、Numpy-科学计算基础库
Numpy: NumPy(Numerical Python) 是科学计算基础库,提供大量科学计算相关功能,比如数据统计,随机数生成等.其提供最核心类型为多维数组类型(ndarray) ...
- Java线程——线程之间的通信
Java中多线程间的通信是怎么实现的? 线程通信的方式: (1)共享变量 线程之间的通信可以通过发送信号,发送信号的一个简单方法就是再共享的对象里面设置信号值.线程A在一个同步块中设置boolean型 ...