【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把英雄联盟,所谓小撸怡情大撸伤身,这个月游戏 ...
随机推荐
- MFC 屏蔽esc跟enter键
BOOL CMenuOperate::PreTranslateMessage(MSG* pMsg) { if(pMsg->message == WM_KEYDOWN && pMs ...
- windows Driver 查询指定键值
NTSTATUS status; HANDLE hKey = NULL; OBJECT_ATTRIBUTES oa; UNICODE_STRING strPath = RTL_CONSTANT_STR ...
- Java算法练习——整数反转
题目链接 题目描述 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 备注 注意: 假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 $[−2^{31}, 2^ ...
- discuz伪静态问题(简单)
提前声明一下我用的是宝塔面板.Linux系统.Nginx Web Server.经过一上午的摸索(我很菜了),终于在一个很无语的地方成功搞了伪静态1.2.点击查看当前的 Rewrite 规则3.我的是 ...
- spring boot 环境配置(profile)切换
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Gym - 101142C CodeCoder vs TopForces(搜索)
题意:给定n个人在两个网站上的得分,一个人若能在任意一个网站里战胜另一个人,则认为这个人能战胜那个人.问每个人都能战胜多少人. 分析: 1.战胜具有传递性. 例如: 4 5 2 7 3 3 因为第三个 ...
- caffe + ssd网络训练过程
參考博客:https://blog.csdn.net/xiao_lxl/article/details/79106837 1获取源代码:git clone https://github.com/wei ...
- 转载电子发烧友网---STM32的IO口灌入电流和输出驱动电流
刚开始学习一款单片机的时候一般都是从操作IO口开始的,所以我也一样,先是弄个流水灯. 刚开始我对STM32的认识不够,以为是跟51单片机类似,可以直接操作端口,可是LED灯却没反应,于是乎,仔细查看资 ...
- JSP编码问题解决方法
最近再看JSP相关知识,被中文乱码搞的很头大.找了好多方法终于找到了一个简单可行的方案. JSP中request和response操作默认编码为"ISO-8859-1",这是中文乱 ...
- C++中substr()详解
#include<string> #include<iostream> using namespace std; int main() { string s("123 ...