UItableViewの注意点

7498 ワード

iOSアプリケーションでは、UItableViewが最も使用率の高いビューの1つになるはずです.iPod、時計、カレンダー、メモ、Mail、天気、写真、電話、メール、Safari、App Store、iTunes、Game Center⋯ほとんどの持参したアプリケーションでその姿を見ることができ、その重要性がわかります.しかしながら、サードパーティ製アプリケーションを使用する場合、パフォーマンス上の問題がしばしば発生し、スクロール時にカード、特にtable cellにピクチャが含まれている場合に一般的に表現される.実際には、的確に最適化すれば、このような問題は起こりません.興味のあるのはLazyTableImagesという公式の例プログラムを見て、ネットから画像をダウンロードして表示することもありますが、スクロールするときは少しもカードがかかりません.次に、UItableViewについての私の理解についてお話しします.しかし、私も初心者なので、言い間違えたり、漏らしたりするかもしれませんので、参考にしてください.まずUITableViewの原理についてお話しします.興味のある方は《About Table Views in iOS-Based Applications》をご覧ください.UItableViewはUIscrollViewのサブクラスであるため、スクロールイベント(一般的には上下スクロール)に自動的に応答することができます.内部には0~複数のUItableViewCellオブジェクトが含まれており、各table cellはそれぞれの内容を示しています.新しいcellが表示される必要がある場合、tableView:cellForRowAtIndexPath:メソッドを呼び出してcellを取得または作成します.見えないときは解放されますこのように、同じ時間に1画面のcellオブジェクトが存在するだけで、各行にcellを作成する必要はありません.また、UItableViewは複数のsectionsに分けられ、各セグメントには独自のhead、foot、cellsがあります.一方、cellを位置決めするには、どのsectionにあるか、このsectionの何行目にあるかの2つのフィールドが必要です.これはiOS SDKでNSIndexPathとして記述されており、UImitはindexPathForRow:inSection:この作成方法を追加しています.他の編集などは、本稿とは関係ないので、言わないでください.原理を紹介してから、最適化を始めましょう.
  • は不透明なビューを使用します.不透明なビューは、レンダリングの速度を大幅に向上させることができます.したがって、必要でない場合は、table cellとそのサブビューのopaqueプロパティをYES(デフォルト)に設定できます.特例には背景色が含まれており、alpha値は1(例えばclearColorを使用しない)であるべきである.画像のalpha値も1にするか、図面を描くときに不透明にする.
  • 不要なtable cellを繰り返し作成しないでください.前述したように、UItableViewは1画面のUItableViewCellオブジェクトだけでよい.したがってcellが表示されない場合はキャッシュし、必要に応じて使用し続けることができます.UItableViewでは、identifierを簡単に設定するだけでよいというメカニズムも提供されています.
    static NSString *CellIdentifier = @"xxx";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
    if (cell == nil) {
    
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    
    }
    は、cellが再利用されると、内部に描かれたコンテンツが自動的に消去されないため、setNeedsDisplayInRect:またはsetNeedsDisplayメソッドを呼び出す必要がある可能性があります.またtable cellを追加する場合、アニメーション効果が必要でない場合は、insertRowsAtIndexPaths:withRowAnimation:メソッドではなく、reloadDataメソッドを直接呼び出すのが望ましい.前者はすべてのindexPathsに対してtableView:cellForRowAtIndexPath:メソッドを呼び出すため、そのcellが表示する必要がなくても(バグかどうかは分からない)、余分なcellを大量に作成することができます.誤り:シミュレータでテストしただけで、本体のデバッグ時にこのようなバグはありません.
  • ビューの数を減らします.UItableView CellにはtextLabel、detailTextLabel、imageViewなどのviewが含まれていますが、contentViewにビューをカスタマイズすることもできます.しかしviewは大きなオブジェクトであり、作成すると多くのリソースが消費され、レンダリングのパフォーマンスにも影響します.table cellに画像が含まれており、数が多い場合は、デフォルトのUItableViewCellを使用するとパフォーマンスに非常に影響します.不思議なことに、定義済みのviewではなく、カスタムviewを使用すると、明らかに速くなります.もちろん、最善の解決策はUItableView Cellを継承し、drawRect:で自分で描画することです.
    - (void)drawRect:(CGRect)rect {    
    
        if (image) {
    
            [image drawAtPoint:imagePoint];
    
            self.image = nil;
    
        } else {
    
            [placeHolder drawAtPoint:imagePoint];
    
        }	
    
        
    
        [text drawInRect:textRect withFont:font lineBreakMode:UILineBreakModeTailTruncation];
    
    }
    ですが、そうすると、行を選択すると、このcellが青くなり、その内容がブロックされていることに気づきます.最も簡単な方法は、cellのselectionsStyleプロパティをUItableViewCellSelectionsStyleNoneに設定することです.これにより、ハイライトされません.また、CALayerを作成し、内容をlayerに描画し、cellのcontentViewを作成することもできます.layerはaddSublayer:メソッドを呼び出します.この例では、layerはパフォーマンスに顕著な影響を及ぼさないが、layerが透明であるか、フィレット、変形などの効果があると、描画速度に影響を及ぼす.解決策としては、後のプリレンダリングイメージを参照してください.
  • 余分な描画作業をしないでください.drawRect:を実装する場合、rectパラメータは描画する必要がある領域であり、この領域以外は描画する必要はありません.たとえば、前述の例では、イメージおよびtextを描画する必要があるかどうかをCGRectIntersectsRect、CGRectIntersection、またはCGRectContainsRectで判断し、描画方法を呼び出すことができます.
  • プリレンダリング画像.上記の点をやり遂げても、新しい画像が現れると、短い停止現象があることがわかります.解決策はbitmap contextでまずそれを描き、UIImageオブジェクトにエクスポートしてから画面に描画することであり、詳細は『プリレンダリングによるiOSデバイスの画像表示の高速化』に表示されます.
  • メインスレッドをブロックしないでください.前の何時までやったら、table viewをスクロールするのに十分スムーズであるはずですが、ユーザーを不快にさせる可能性があります.よくある現象は、データの更新時にインタフェース全体が動かず、ユーザーの要求に全く応答しないことです.この現象の原因は、メインスレッドが時間のかかる関数またはメソッドを実行し、実行が完了するまで画面を描画したり、ユーザーの要求に応答したりできないためです.最も一般的なのは、ネットワークリクエストです.通常は数秒かかりますが、ユーザーをそんなに長く待たせるべきではありません.解決策は、マルチスレッドを使用して、サブスレッドにこれらの関数またはメソッドを実行させることです.ダウンロードスレッド数が2を超えると、プライマリスレッドのパフォーマンスに著しく影響するという学問もあります.したがって、ASIHTTPRequestを使用する場合、ダウンロード要求を1つのNSOperationQueueで維持し、maxConcurrentOperationCountを2に設定することができます.NSURLRequestはGCDと連携して実現したり、NSURLConnectionのsetDelegateQueue:メソッドを使用したりすることができます.もちろん、ユーザの要求に応答する必要がない場合には、ダウンロードスレッド数を増やしてダウンロード速度を速めることもできます.
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    
        if (!decelerate) {
    
            queue.maxConcurrentOperationCount = 5;
    
        }
    
    }
    
    
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    
        queue.maxConcurrentOperationCount = 5;
    
    }
    
    
    
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    
        queue.maxConcurrentOperationCount = 2;
    
    }
    また、更新データを自動的にロードすることは、ユーザにとっても友好的であり、ダウンロードを待つ時間を短縮します.例えば50個の情報をロードするたびに、最後から10番目の情報までスクロールするときに、より多くの情報をロードすることができます:
    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    
        if (count - indexPath.row < 10 && !updating) {
    
            updating = YES;
    
            [self update];
    
        }
    
    }
    
    // update        ,  updating NO
    さらに注意しなければならないのは、画像のダウンロードが完了した後、cellが表示されている場合、画像を更新する必要があります:
    NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
    
    for (NSIndexPath *visibleIndexPath in indexPaths) {
    
        if (indexPath == visibleIndexPath) {
    
            MyTableViewCell *cell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
    
            cell.image = image;
    
            [cell setNeedsDisplayInRect:imageRect];
    
            break;
    
        }
    
    }
    
    //      ,        ,        。
    最後に、前述したinsertRowsAtIndexPaths:withRowAnimation:方法、新しい行を挿入するにはメインスレッドで実行する必要があります.一度に多くのローを挿入すると(たとえば50ロー)、メインスレッドが長時間ブロックされます.reloadDataメソッドに置き換えると、瞬時に処理が完了します.

  • そんなにたくさん言って、私はこれらをやり遂げるのも悪くないと思って、その他は自分でprofileして、ボトルネックを見つけて最適化する必要があります.