Ответ 1
Как написано в комментариях, если основная потребность - знать, КОГДА обновлять слой (а не как), я переношу свой оригинальный ответ после строки "OLD ANSWER" и добавляю то, что обсуждается в комментариях:
Сначала (100% Apple Review Safe; -)
- Вы можете выполнять периодические "скриншоты" вашего исходного UIView и сравнивать полученные NSData (старые и новые) → , если данные разные, содержимое слоя изменилось. Нет необходимости сравнивать скриншоты FULL RESOLUTION, но вы можете сделать это с меньшим, чтобы иметь лучшую производительность.
Во-вторых: дружественный к производительности и "теоретический" обзор безопасен... но не уверен: -/
- По этой ссылке http://www.lombax.it/documents/DirtyLayer.zip вы можете найти образец проекта, который предупреждает вас каждый раз, когда слой UIWebView становится грязным; -)
Я пытаюсь объяснить, как я пришел к этому коду:
Основная цель - понять, когда TileLayer (частный подкласс CALayer, используемый UIWebView) становится грязным.
Проблема заключается в том, что вы не можете получить к ней доступ напрямую. Но вы можете использовать метод swizzle для изменения поведения метода layerSetNeedsDisplay: в каждом CALayer и подклассах.
Вы должны обязательно избегать радикального изменения исходного поведения и делать только необходимое, чтобы добавить "уведомление" при вызове метода.
Когда вы успешно обнаружили каждый вызов layerSetNeedsDisplay: call, единственное, что осталось, - это понять, "что есть" задействованный CALayer → если он является внутренним UIWebView TileLayer, мы запускаем уведомление "isDirty".
Но мы не можем перебирать содержимое UIWebView и находить TileLayer, например, просто используя "isKindOfClass: [TileLayer class]", вы обязательно откажетесь (Apple использует статический анализатор для проверки использования частного API), Что вы можете сделать?
Что-то сложное, например... например, сравните задействованный размер слоя (тот, который вызывает layerSetNeedsDisplay:) с размером UIWebView?; -)
Кроме того, иногда UIWebView изменяет дочерний TileLayer и использует новый, поэтому вам нужно сделать это еще раз.
Последняя вещь: layerSetNeedsDisplay: не всегда вызывается, когда вы просто просматриваете UIWebView (если слой уже построен), поэтому вам нужно использовать UIWebViewDelegate для перехвата прокрутки/масштабирования.
Вы обнаружите, что этот метод swizzle является причиной отказа в некоторых приложениях, но он всегда мотивирован тем, что вы "изменили поведение объекта". В этом случае вы не меняете поведение чего-то, а просто перехватываете, когда вызывается метод. Я думаю, что вы можете попробовать попробовать или связаться с Apple Support, чтобы проверить, является ли это законным, если вы не уверены.
СТАРЫЙ ОТВЕТ
Я не уверен, что это достаточно дружелюбная производительность, я пробовал это только с обоими представлениями на одном устройстве, и он работает очень хорошо... вы должны попробовать его с помощью Airplay.
Решение довольно просто: вы берете "скриншот" UIWebView/MKMapView, используя UIGraphicsGetImageFromCurrentImageContext. Вы делаете это 30/60 раз в секунду и копируете результат в UIImageView (видимый на втором дисплее, вы можете перемещать его там, где хотите).
Чтобы определить, изменился ли вид и не трафик на беспроводной линии, вы можете сравнить байты байтов uiimages (старый кадр и новый кадр) и установить новое, только если оно отличается от предыдущего. (да, это работает!; -)
Единственное, что мне сегодня не удалось, это быстро сделать это сравнение: если вы посмотрите на пример кода, вы увидите, что сравнение действительно интенсивное (потому что он использует UIImagePNGRepresentation() для преобразования UIImage в NSData) и делает все приложение настолько медленным. Если вы не используете сравнение (копирование каждого кадра), приложение работает быстро и плавно (по крайней мере, на моем 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 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];
}