`
dadi520
  • 浏览: 145694 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Asynchronous UITableViewCell content loading done right

    博客分类:
  • ios
 
阅读更多

http://stavash.wordpress.com/2012/12/14/advanced-issues-asynchronous-uitableviewcell-content-loading-done-right/

Haven’t you always wondered why your UITableView is loading “almost” perfectly? I mean, sure- you’ve made it clear to the iOS that all non-trivial cell work (such as downloading images from a remote URL or rendering content) is to be computed asynchronously on a background thread. But sometimes this is not enough, mainly for 2 reasons:

1. Once a cell is out of the visible area, the asynchronous operation you called is still doing work. This often results in unnecessary system resource usage or even buggy table behaviour caused by operations not knowing which cell to return to when they’re done.

2 . UITableViewCells are often reused instances. This means that cells being loaded into the view may sometimes contain data that was loaded originally into a completely different cell. This often causes a “Cell switching” behaviour which can just completely piss you off.

Solved!

First of all it’s important to understand that there are many ways to tackle this issue – some are great ideas and some are, well, awful. The method that I’m about to describe here is based on a demo provided by Apple (namely WWDC 2012 session 211), and you know those guys know a thing or two about iOS.

For our example, I’ll use a simple UITableView instance that is meant for displaying your facebook friends’ names and profile pictures. The main idea is that we start loading the profile images when UITableViewDataSource’s tableView:cellForRowAtIndexPath: is called. If the operation succeeds and the cell is still in view then we simply add the image to the cell’s profile image view (on the main thread). If it’s not – we make sure not to perform the “setImage” part.

Before you start, some prep work: Define an NSOperationQueue for running background operations – in this example, we call it the imageLoadingOperationQueue. Also, define an NSMutableDictionary for storing references to specific operations – in this example we will map the facebook unique ids to the operations on the facebookUidToImageDownloadOperations dictionary.

Most of the important stuff is commented in the code so make sure you read the comments to understand what’s going on:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    FacebookFriend *friend = [self.facebookFriends objectAtIndex:indexPath.row];
    FacebookFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:FB_CELL_IDENTIFIER forIndexPath:indexPath];
    cell.lblName.text = friend.name;

    //Create a block operation for loading the image into the profile image view
    NSBlockOperation *loadImageIntoCellOp = [[NSBlockOperation alloc] init];
    //Define weak operation so that operation can be referenced from within the block without creating a retain cycle
    __weak NSBlockOperation *weakOp = loadImageIntoCellOp;
    [loadImageIntoCellOp addExecutionBlock:^(void){
        //Some asynchronous work. Once the image is ready, it will load into view on the main queue
        UIImage *profileImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:friend.imageUrl]]];
        [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
            //Check for cancelation before proceeding. We use cellForRowAtIndexPath to make sure we get nil for a non-visible cell
            if (!weakOp.isCancelled) {
                FacebookFriendCell *theCell = (FacebookFriendCell *)[tableView cellForRowAtIndexPath:indexPath];
                [theCell.ivProfile setImage:profileImage];
                [self.facebookUidToImageDownloadOperations removeObjectForKey:friend.uid];
            }
        }];
    }];

    //Save a reference to the operation in an NSMutableDictionary so that it can be cancelled later on
    if (friend.uid) {
        [self.facebookUidToImageDownloadOperations setObject:loadImageIntoCellOp forKey:friend.uid];
    }

    //Add the operation to the designated background queue
    if (loadImageIntoCellOp) {
        [self.imageLoadingOperationQueue addOperation:loadImageIntoCellOp];
    }

    //Make sure cell doesn't contain any traces of data from reuse -
    //This would be a good place to assign a placeholder image
    cell.ivProfile.image = nil;

    return cell;
}

Now all that’s left to do is to take advantage of a new UITableViewDelegate method introduced in iOS 6.0: It’s called “tableView:didEndDisplayingCell:forRowAtIndexPath:” and It’s called right after the cell we are loading our data into is no longer needed. Sounds like a perfect spot for the following code, which fetches the relevant operation and cancels it:

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    FacebookFriend *friend = [self.facebookFriends objectAtIndex:indexPath.row];
    //Fetch operation that doesn't need executing anymore
    NSBlockOperation *ongoingDownloadOperation = [self.facebookUidToImageDownloadOperations objectForKey:friend.uid];
    if (ongoingDownloadOperation) {
        //Cancel operation and remove from dictionary
        [ongoingDownloadOperation cancel];
        [self.facebookUidToImageDownloadOperations removeObjectForKey:friend.uid];
    }
}

Also, don’t forget to take advantage of the NSOperationQueue and call “cancelAllOperations” when the table is not needed anymore:

- (void)viewDidDisappear:(BOOL)animated {
    [self.imageLoadingOperationQueue cancelAllOperations];
}

That’s it! You now have yourself a UITableView running as smooth as a Ferrari. You’re welcome

分享到:
评论

相关推荐

    jQuery dynamic content loading.zip

    在"jQuery-dynamic-content-loading-whilst-preserving-page-history-master"这个文件夹中,通常包含了项目的源码、示例、文档等相关文件。开发者可以通过阅读源码了解其工作原理,或者直接在自己的项目中引用这些...

    Semantics of Asynchronous JavaScript

    Since the JavaScript language is single threaded, Node.js programs must make use of asynchronous callbacks and event loops managed by the runtime to ensure appli- cations remain responsive....

    页面loading插件

    AJAX(Asynchronous JavaScript and XML)允许网页在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容。然而,这种技术在请求和接收数据的过程中,用户可能会感知到页面的停滞,导致体验下降。这时...

    Asynchronous Programming Patterns .net异步编程模型

    The .NET Framework provides three patterns for performing asynchronous operations: Asynchronous Programming Model (APM) pattern Event-based Asynchronous Pattern (EAP) Task-based Asynchronous Pattern ...

    Asynchronous Design Methodologies.pdf

    Asynchronous design has been an active area of research since at least the mid 1950’s, but has yet to achieve widespread use. We examine the benefits and problems inherent in asynchronous ...

    Pro Asynchronous Programming with .NET

    Pro Asynchronous Programming with .NET teaches the essential skill of asynchronous programming in .NET. It answers critical questions in .NET application development, such as: how do I keep my program...

    Asynchronous Methods for Deep Reinforcement Learning

    learning that uses asynchronous gradient descent for optimization of deep neural network controllers. We present asynchronous variants of four standard reinforcement learning algorithms and show that ...

    Asynchronous Android Programming

    "Asynchronous Android Programming" English | ISBN: 1785883240 | 2016 | 394 pages About This Book Construct scalable and performant applications to take advantage of multi-thread asynchronous ...

    [EBOOK]Asynchronous Circuit Design--Chris.J.Myers(包含源代码).rar

    [EBOOK]Asynchronous Circuit Design--Chris.J.Myers(包含源代码) With asynchronous circuit design becoming a powerful tool in the development of new digital systems, circuit designers are expected to ...

    AJAX (Asynchronous JavaScript And XML)

    AJAX (Asynchronous JavaScript And XML)

    The Task-based Asynchronous Pattern(TAP)

    The Task-based Asynchronous Pattern (TAP) is a new pattern for asynchrony in the .NET Framework. It is based on the Task and Task<TResult> types in the System.Threading.Tasks namespace, which are used...

    Asynchronous External Memory Interface (EMIF)

    Asynchronous External Memory Interface(EMIF)是一种用于芯片与外部存储器进行通信的接口技术。它是德州仪器(Texas Instruments,简称TI)公司为其TMS320DM36x数字媒体系统芯片(DMSoC)所提供的一个外围设备。...

    my broswer Asynchronous Pluggable Protocol

    Asynchronous Pluggable Protocol(简称APR)是Microsoft为Windows平台设计的一种机制,允许开发者创建自定义的网络协议,这些协议可以被应用程序异步地使用,从而提高系统性能并优化用户体验。 在给定的文件列表中...

    Asynchronous Message Communication

    异步消息通信技术是在不同进程、不同LabVIEW目标(网络上的系统)以及LabVIEW应用内发送消息的通用LabVIEW API。这种技术架构的关键点在于异步消息通信(AMC)库,它通过提供一组函数和工具,让开发者可以在LabVIEW...

    Asynchronous FIFO Architectures

    异步FIFO(First-In-First-Out)架构是数字系统设计中的一个重要概念,尤其是在高速数据传输和通信接口中。FIFO是一种特殊的存储结构,其中数据按照先入先出的顺序进行读取和写入,类似于现实生活中的排队系统。...

    Asynchronous Procedure Calls in NT

    在Windows NT操作系统中,异步过程调用(Asynchronous Procedure Calls, APC)是一种重要的技术,用于在特定的线程上下文中执行任务,而无需线程持续监视或轮询。这种技术在Windows NT内核中扮演着重要角色,尤其是...

    loading图片素材

    "loading图片素材"的标题表明这是一组专为这种目的设计的图像资源,适合于在进行页面AJAX(Asynchronous JavaScript and XML)操作时使用。 AJAX 是一种创建动态网页的技术,允许在不刷新整个页面的情况下与服务器...

    Combine Asynchronous Programming with Swift.zip

    The introduction of Combine into the Swift ecosystem now gives you a native way to manage asynchronous events in Swift, meaning you don’t have to rely on third-party reactive frameworks for event-...

    loading所需资源

    例如,可以使用AJAX(Asynchronous JavaScript and XML)或者Fetch API来异步获取数据,并通过JavaScript控制DOM元素来显示或隐藏loading图标。 结合CSS和JS,我们可以创建高度自定义的loading插件。这样的插件通常...

Global site tag (gtag.js) - Google Analytics