コメントに書かれているように、主な必要性がいつレイヤーを更新するか (方法ではなく) を知ることである場合、元の回答を「OLD ANSWER」行の後に移動し、コメントで説明した内容を追加します。
まず (100% Apple Review Safe ;-)
- 元の UIView の定期的な「スクリーンショット」を取得し、結果の NSData (古いものと新しいもの) を比較できます --> データが異なる場合、レイヤーのコンテンツが変更されます。フル解像度のスクリーンショットを比較する必要はありませんが、小さい方のスクリーンショットで比較すると、パフォーマンスが向上します。
2番目:パフォーマンスに優しく、「理論的に」レビューは安全ですが、よくわかりません:-/
このコードにたどり着いた方法を説明しようとします。
主な目標は、TileLayer (UIWebView で使用される CALayer のプライベート サブクラス) がいつダーティになるかを理解することです。
問題は、直接アクセスできないことです。ただし、swizzle メソッドを使用して、すべての CALayer およびサブクラスで layerSetNeedsDisplay: メソッドの動作を変更できます。
元の動作を大幅に変更することは避け、メソッドが呼び出されたときに「通知」を追加するために必要なことだけを行う必要があります。
各 layerSetNeedsDisplay: 呼び出しを正常に検出したら、あとは関連する CALayer が「どれか」を理解するだけです。つまり、それが内部 UIWebView TileLayer である場合は、「isDirty」通知をトリガーします。
しかし、UIWebView のコンテンツを反復処理して TileLayer を見つけることはできません。たとえば、単に「isKindOfClass:[TileLayer class]」を使用すると確実に拒否されます (Apple は静的アナライザーを使用してプライベート API の使用をチェックします)。あなたは何ができますか?
たとえば...関連するレイヤーのサイズ(layerSetNeedsDisplay:を呼び出しているもの)とUIWebViewのサイズを比較するなどのトリッキーなものはありますか?;-)
さらに、UIWebView が子の TileLayer を変更して新しいものを使用する場合があるため、このチェックを何度も行う必要があります。
最後に: layerSetNeedsDisplay: は、単に UIWebView をスクロールするだけでは呼び出されないため (レイヤーが既に構築されている場合)、UIWebViewDelegate を使用してスクロール/ズームをインターセプトする必要があります。
メソッドのスウィズルが一部のアプリで拒否される理由であることがわかりますが、それは常に「オブジェクトの動作を変更した」という動機で行われています。この場合、何かの動作を変更するのではなく、メソッドが呼び出されたときにインターセプトするだけです。よくわからない場合は、試してみるか、Apple サポートに連絡して合法かどうかを確認してください。
古い答え
これがパフォーマンスに十分に適しているかどうかはわかりません。同じデバイスの両方のビューでのみ試してみましたが、かなりうまく機能します...Airplayを使用して試してみてください.
解決策は非常に簡単です。UIGraphicsGetImageFromCurrentImageContext を使用して UIWebView / MKMapView の「スクリーンショット」を撮ります。これを 1 秒間に 30/60 回行い、結果を UIImageView にコピーします (2 番目のディスプレイに表示され、好きな場所に移動できます)。
ビューが変更されたかどうかを検出し、ワイヤレス リンクでのトラフィックを回避するには、2 つの uiimage (古いフレームと新しいフレーム) をバイト単位で比較し、以前と異なる場合にのみ新しいフレームを設定します。(ええ、うまくいきます!;-)
今夜私ができなかった唯一のことは、この比較を高速化することです: 添付のサンプル コードを見ると、比較が実際に CPU を集中的に使用することがわかります (UIImagePNGRepresentation() を使用して NSData の UIImage を変換するため)。アプリ全体が非常に遅くなります。比較 (すべてのフレームをコピー) を使用しない場合、アプリは高速でスムーズです (少なくとも私の iPhone 5 では)。しかし、それを解決する可能性は非常に高いと思います...たとえば、4〜5フレームごとに比較を行うか、バックグラウンドでNSDataの作成を最適化します
サンプル プロジェクトを添付します: http://www.lombax.it/documents/ImageMirror.zip
プロジェクトでは、フレーム比較が無効になっています (コメントが付けられている場合)。今後の参考のために、ここにコードを添付します。
// here you start a timer, 50fps
// the timer is started on a background thread to avoid blocking it when you scroll the webview
- (IBAction)enableMirror:(id)sender {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); //0ul --> unsigned long
dispatch_async(queue, ^{
// 0.04f --> 25 fps
NSTimer __unused *timer = [NSTimer scheduledTimerWithTimeInterval:0.02f target:self selector:@selector(copyImageIfNeeded) userInfo:nil repeats:YES];
// need to start a run loop otherwise the thread stops
CFRunLoopRun();
});
}
// this method create an UIImage with the content of the given view
- (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
// the method called by the timer
-(void)copyImageIfNeeded
{
// this method is called from a background thread, so the code before the dispatch is executed in background
UIImage *newImage = [self imageWithView:self.webView];
// the copy is made only if the two images are really different (compared byte to byte)
// this comparison method is cpu intensive
// UNCOMMENT THE IF AND THE {} to enable the frame comparison
//if (!([self image:self.mirrorView.image isEqualTo:newImage]))
//{
// this must be called on the main queue because it updates the user interface
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
self.mirrorView.image = newImage;
});
//}
}
// method to compare the two images - not performance friendly
// it can be optimized, because you can "store" the old image and avoid
// converting it more and more...until it's changed
// you can even try to generate the nsdata in background when the frame
// is created?
- (BOOL)image:(UIImage *)image1 isEqualTo:(UIImage *)image2
{
NSData *data1 = UIImagePNGRepresentation(image1);
NSData *data2 = UIImagePNGRepresentation(image2);
return [data1 isEqual:data2];
}