8

我刚刚创建了一个UICollectionView用户可以将手机中的图像添加到应用程序中的相册功能的功能。我将图像保存到文档目录中的 a 子目录中,以便可以添加和删除更多图像。但是,当我向上和向下滚动集合视图时,它非常滞后。

我怎样才能使滚动美观流畅?

我的代码:前 16 张图片是预设图片,之后的所有图片都来自文档目录中的子目录

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Custom" forIndexPath:indexPath];
    //Current index number
    int index=indexPath.section * noOfSection + indexPath.row;
    //Check if its the preset photos
    if(index<16){
        NSString *name=[recipePhotos objectAtIndex:indexPath.section * noOfSection + indexPath.row];
        cell.imageView.image=[UIImage imageNamed:name];
    }

//not preset photos, so retrieve the photos the user added
    else {
        NSData *data= [NSData dataWithContentsOfFile:[recipePhotos objectAtIndex:index]];
        UIImage *theImage=[UIImage imageWithData:data];

        cell.imageView.image=theImage;
        data=nil;
    }

    return cell;
}

Time Profiler 给了我这个

Running Time    Self        Symbol Name
568.0ms   63.1% 0.0     Main Thread  0x4048
320.0ms   35.5% 0.0     _pthread_start  0x405e
320.0ms   35.5% 0.0      thread_start
320.0ms   35.5% 0.0       _pthread_start
320.0ms   35.5% 0.0        0x1084be960
310.0ms   34.4% 1.0         0x1084be6f0
7.0ms    0.7%   0.0         mach_msg
2.0ms    0.2%   2.0         objc_msgSend
1.0ms    0.1%   1.0         -[NSAutoreleasePool release]
4.0ms    0.4%   0.0     _dispatch_mgr_thread  0x4052
4.0ms    0.4%   0.0      _dispatch_mgr_thread
4.0ms    0.4%   0.0       _dispatch_mgr_invoke
4.0ms    0.4%   4.0        kevent
3.0ms    0.3%   0.0     _dispatch_worker_thread2  0x62b24
3.0ms    0.3%   1.0      start_wqthread
3.0ms    0.3%   0.0     _dispatch_worker_thread2  0x62a84
3.0ms    0.3%   0.0      start_wqthread
3.0ms    0.3%   0.0       _pthread_wqthread
3.0ms    0.3%   0.0        _dispatch_worker_thread2
3.0ms    0.3%   0.0         _dispatch_queue_invoke
3.0ms    0.3%   0.0          _dispatch_queue_drain
3.0ms    0.3%   0.0           _dispatch_client_callout
2.0ms    0.2%   0.0            my_io_execute_passive_block
1.0ms    0.1%   0.0             __86-[NSPersistentUIManager writePublicPlistWithOpenWindowIDs:optionallyWaitingUntilDone:]_block_invoke_0835
1.0ms    0.1%   0.0              -[NSPersistentUIManager writePublicPlistData:]
1.0ms    0.1%   0.0               -[NSURL(NSURLPathUtilities) URLByAppendingPathComponent:]
1.0ms    0.1%   0.0                -[NSURL getResourceValue:forKey:error:]
1.0ms    0.1%   0.0                 CFURLCopyResourcePropertyForKey
1.0ms    0.1%   0.0             __block_global_2
1.0ms    0.1%   0.0              -[NSPersistentUIManager writeRecords:withWindowInfos:flushingStaleData:]
1.0ms    0.1%   0.0            _dispatch_call_block_and_release
1.0ms    0.1%   0.0             0x1084b8580
1.0ms    0.1%   0.0              mach_msg_send
1.0ms    0.1%   0.0               mach_msg
1.0ms    0.1%   1.0                mach_msg_trap
1.0ms    0.1%   0.0     _pthread_struct_init  0x62a83
1.0ms    0.1%   0.0      start_wqthread
1.0ms    0.1%   0.0       _pthread_wqthread
1.0ms    0.1%   1.0        _pthread_struct_init
1.0ms    0.1%   0.0     start_wqthread  0x62a7f
4

4 回答 4

4

您将需要采取一种类似于您需要在表格视图中执行的方法,您将需要重用视图,就像您在表格视图中重用您的单元格一样。

Ray Wenderlich 提供的一个非常好的教程:

在第一部分你有基本的,在第二部分他们谈论可重用的视图,你看一下链接:

http://www.raywenderlich.com/22417/beginning-uicollectionview-in-ios-6-part-22

编辑

异步加载图像的示例:

例如,在您的单元格中创建一个方法loadImageFromFile,该方法接收您将以这种方式调用它的路径:

[cell loadImageFromFile:[recipePhotos objectAtIndex:index]];

然后看起来像(也许你需要适应一些东西......):

- (void) loadImageFromFile:(NSString*)path{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
        NSData *data= [NSData dataWithContentsOfFile:path];
        UIImage *theImage=[UIImage imageWithData:data];

        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image=theImage;
    });
});
于 2013-05-25T02:08:42.200 回答
4

@ggrana 有正确的想法。加载异步肯定会有所帮助。但是,如果您每次都从文件加载,您仍然在做多余的工作。要考虑的一件事是使用NSCache. 它基本上是一个NSDictionary但自己进行内存管理并在存在内存压力时转储数据。

因此,如果您有内存预算,您实际上可以即时制作缩略图(因此您不必硬编码大小)并将它们存储在缓存中。这样,您的图像只会在第一次弹出。之后的每次,它们都会立即加载。

你可以像这样使用它:

@implementation ...
{
    NSCache * _johnny;  // get it?
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    [cell setImage:nil]; // since they are reused, prevents showing an old image
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
         UIImage * sticker = [self thumbnailOfSize:CGSizeMake(desiredImageWidth, desiredImageHeight) 
                                          forIndex:[indexPath row]];
         dispatch_async(dispatch_get_main_queue(), ^{
             [cell setImage:sticker];
         });
    });
}

// load image from disk and cache thumbnail version
- (UIImage*) thumbnailOfSize:(CGSize)size forIndex:(NSInteger)index
{
    NSString * cacheKey = [NSString stringWithFormat:@"%@ %d", NSStringFromCGSize(size), index];
    UIImage * image = [_johnny objectForKey:cacheKey];

    if (!image) {
        image = [UIImage imageWithContentsOfFile:_imagePaths[index]];

        float desiredWidth = size.width;
        float desiredHeight = size.height;
        float actualHeight = image.size.height;
        float actualWidth = image.size.width;
        float imgRatio = actualWidth / actualHeight;
        float maxRatio = desiredWidth / desiredHeight;

        if(imgRatio != maxRatio) {
            if(imgRatio < maxRatio) {
                imgRatio = desiredHeight / actualHeight;
                actualWidth = imgRatio * actualWidth;
                actualHeight = desiredHeight;
            } else {
                imgRatio = desiredWidth / actualWidth;
                actualHeight = imgRatio * actualHeight;
                actualWidth = desiredWidth;
            }
        }

        CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
        UIGraphicsBeginImageContextWithOptions(rect.size, FALSE, 0); // do right thing for retina
        [image drawInRect:rect];
        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        // here's johnny
        [_johnny setObject:image forKey:cacheKey];
    }

    return image;
}
于 2014-02-21T18:04:09.470 回答
3

因此,经过一番折腾,我发现问题出在几个因素上。

一个-缩略图的图像太大,所以我做了一个单独的图像数组,图像尺寸较小,适合单元格。二- 在@ggrana 的帮助下,打开一个单独的线程加快了进程并减少了延迟。三 - 我还发现拥有图像数组而不是图像位置更快 - 唯一的问题是它占用更多内存。

于 2013-05-29T17:50:41.223 回答
1
fileprivate func downloadForPerformance()->void{
   for i in urlToFetchImageArray {
       var image: UIImage = dowloadImageFunc(link: i.urlString() );
       imageArray.append( image );
   }
}
fileprivate func dowloadImageFunc(link: String)->UIImage {
    let url = URL(string: link)
    let data = try? Data(contentsOf: url!)
    return UIImage(data: data!) ?? UIImage(named: "default.png")!;
}


cell?.mainImage.image = self.imageArrayPerformance[indexPath.row];

通常问题是下载速度慢,因为每个单元都出队。

  1. 在视图出现之前调用 downloadForPerformance 函数。
  2. 这将填充一个名为 imageArray 的数组作为全局变量
  3. 然后在 cellForItem 函数中使用该数组
于 2021-08-20T05:27:35.997 回答