web-dev-qa-db-ja.com

ビューを画像に高速でレンダリングする方法は?

ユーザーが画面に触れて指を動かすことができる拡大鏡アプリを作成しています。指の経路に拡大鏡があります。スクリーンショットを撮って実装し、次のように画像を拡大鏡の画像ビューに割り当てます。

    CGSize imageSize = frame.size;
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(c, scaleFactor, scaleFactor);
    CGContextConcatCTM(c, CGAffineTransformMakeTranslation(-frame.Origin.x, -frame.Origin.y));
    [self.layer renderInContext:c];
    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return screenshot;

問題はself.layer renderInContextが遅いため、ユーザーが指を動かしているときにスムーズに感じられないことです。他のスレッドでself.layer renderInContextを実行しようとしましたが、拡大鏡の画像に遅延があったため、拡大鏡の画像が奇妙に見えました。

ビューを画像にレンダリングするより良い方法はありますか? renderInContext:はGPUを使用しますか?

22
NOrder

いいえ。iOS6では、renderInContext:が唯一の方法です。遅いです。 CPUを使用します。

UIKitコンテンツをレンダリングする方法

renderInContext:

[view.layer renderInContext:UIGraphicsGetCurrentContext()];
  • IOS 2.0が必要です。 CPUで実行されます
  • 非アフィン変換、OpenGL、またはビデオコンテンツを含むビューはキャプチャしません。
  • アニメーションが実行されている場合は、キャプチャするオプションがあります:
    • view.layer、アニメーションの最終フレームをキャプチャします。
    • view.presentationLayer、アニメーションの現在のフレームをキャプチャします。

snapshotViewAfterScreenUpdates:

UIView *snapshot = [view snapshotViewAfterScreenUpdates:YES];
  • IOS 7が必要です。
  • 最速の方法です。
  • ビューcontentsは不変です。あなたが効果を適用したい場合は良くありません。
  • すべてのコンテンツタイプ(UIKit、OpenGL、またはビデオ)をキャプチャします。

resizableSnapshotViewFromRect:afterScreenUpdates:withCapInsets

[view resizableSnapshotViewFromRect:rect afterScreenUpdates:YES withCapInsets:edgeInsets]
  • IOS 7が必要です。
  • snapshotViewAfterScreenUpdates:と同じですが、サイズ変更可能なインセットを備えています。 contentも不変です。

drawViewHierarchyInRect:afterScreenUpdates:

[view drawViewHierarchyInRect:rect afterScreenUpdates:YES];
  • IOS 7が必要です。
  • 現在のコンテキストで描画します。
  • セッション226によれば、renderInContext:よりも高速です。

新しいスナップショットAPIについては、WWDC 2013セッション 226 iOSでのEngaging UIの実装 を参照してください。


それが何らかの助けである場合は、実行中のキャプチャ試行を破棄するコードを次に示します。

これにより、ブロックの実行が一度に1つに抑制され、他は破棄されます。 this SO answer から。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t renderQueue = dispatch_queue_create("com.throttling.queue", NULL);

- (void) capture {
    if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) == 0) {
        dispatch_async(renderQueue, ^{
            // capture
            dispatch_semaphore_signal(semaphore);
        });
    }
}

これは何をしているのですか?

  • 1つのリソースのセマフォを作成します。
  • シリアルキューを作成します。
  • DISPATCH_TIME_NOWは、タイムアウトがなしであることを意味するため、赤色の信号ですぐにゼロ以外を返します。したがって、ifコンテンツは実行されません。
  • 緑色のライトの場合、ブロックを非同期で実行し、緑色のライトを再度設定します。
83
Jano