3

NSData から画像を読み込むときに、テーブルビューをスムーズにスクロールさせています。

いくつかの背景...画像NSDataをコアデータに保存しています(「外部ストレージを許可する」オプションを使用しているため、URLなどを保存することはここでの解決策ではないと思います)。さらに、UIImagePNGRepresentation を使用した高解像度と UIImageJPGRepresentation を使用した低解像度の 2 種類のデータを保存しています。テーブル ビューの低解像度画像を読み込んでいます。エントリ オブジェクトのイメージ プロパティはコア データに格納されず、事後に設定されます。これが私がやっている方法です:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{        

    DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:@"LogCell" forIndexPath:indexPath];

    [cell setGradients:@[[UIColor whiteColor], [UIColor lightTextColor]]];
    Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UIImage *entryImage = entry.image;

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        NSOperationQueue *oQueue = [[NSOperationQueue alloc] init];
        [oQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"defaultImage.png"];
            entry.image = image;
        }];
        [oQueue addOperationWithBlock:^(void) {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;
            }];
        }];

    }

    return cell;
}

これをよりスムーズに実行する方法についてのアイデアはありますか? どんな助けでも大歓迎です!

ありがとう

アップデート

これでかなり近づきましたが、まだ少しぎくしゃくしています。. . (cellForRowAtIndexPath内)

    UIImage *entryImage = entry.image;

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        [self.imageQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"bills_moduleIcon.png"];
            entry.image = image;
            [entry.image preload];

            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;
            }];
        }];
    }

そのプリロード メソッドは UIImageView のカテゴリにあり、次のようになります。

- (void)preload
{
    CGImageRef ref = self.CGImage;
    size_t width = CGImageGetWidth(ref);
    size_t height = CGImageGetHeight(ref);
    CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, space, kCGImageAlphaPremultipliedFirst);
    CGColorSpaceRelease(space);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), ref);
    CGContextRelease(context);
}

私の次の攻撃計画は、保存されている画像のサイズをいじること (コメントで述べたように) か、テーブルのスクロール中に imageViews を更新しないようにすることだと思います。これら 2 つの方法についてのご意見をお待ちしております。私がそれをしたときに戻ってチェックインする病気

更新 2 これで私を助けてくれたすべての人に感謝します! 最終的な解決策は次のとおりです (cellForRowAtIndexPath:

    if (entryImage)
        cell.rightImageView.image = entryImage;
    else {
        [self.imageQueue addOperationWithBlock:^(void) {
            UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
            if (!image) image = [UIImage imageNamed:@"bills_moduleIcon.png"];
            entry.image = [image scaleToSize:cell.rightImageView.frame.size];

            [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
                DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
                cell.rightImageView.image = entry.image;                
            }];
        }];
    }

UIImage に新しいカテゴリ メソッドを追加します。

-(UIImage*)scaleToSize:(CGSize)size
{
    UIGraphicsBeginImageContext(size);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

スクロールが完全にスムーズになりました。素晴らしい

4

2 に答える 2

2

いくつかの問題:

  1. これらの 2 行にどれくらいの時間がかかっているかを確認したいと思います。

    Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
    UIImage *entryImage = entry.image;
    

    entry.imageこれがキャッシュされた画像であると私は収集していますがobjectAtIndexPath:indexPath、実際にNSDataCore Data から取得していないという自信はありません (これは、ファイル システムではなくローカル データベースに画像を保存する際のパフォーマンスの問題のかなりの部分です)。バックグラウンドキューでこれを行う傾向があります。

  2. に 2 つの操作を追加する理由がわかりませんoQueue(同時に操作できます!)。次のようにする必要があるため、画像が取得されるまでメイン キューにディスパッチしようとしません。

    [oQueue addOperationWithBlock:^(void) {
        UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
        if (!image) image = [UIImage imageNamed:@"defaultImage.png"];
        entry.image = image;
    
        [[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
            DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
            cell.rightImageView.image = entry.image;
        }];
    }];
    
  3. おそらく重要ではありませんが、NSOperationQueueセルごとに新しいものを作成するべきではありません。であるクラス プロパティを定義し、NSOperationQueueで初期化してからviewDidLoad、そのキューを で使用する必要がありますcellForRowAtIndexPath。確かに重要ではありませんが、テーブルの行ごとにキューを作成するオーバーヘッドに見合う価値はありません。(また、サーバーベースの画像取得を行う場合は、単一のキューを使用することが非常に重要であり、maxConcurrentOperationCount.

于 2013-01-27T22:33:13.097 に答える
0

UIImage は、画像を描画する必要があるまで実際にデコードを実行しないため、画像を画像ビューに割り当てると、実際にはメインスレッドで発生します。

ここに、画像を強制的にデコードする UIImage カテゴリがあります (バックグラウンド スレッドで実行する必要があります): https://github.com/tonymillion/TMDiskCache

于 2013-01-27T22:10:23.377 に答える