2013-06-11 3 views
23

Я пытаюсь использовать CAShapeLayer, чтобы замаскировать CALayer в моем iOS приложения, как это занимает долю CPU time маскировать изображение против вручную маскировок один в bitmap context;Проблемы производительности при использовании Многих CALayer маски

Когда у меня есть несколько десятков или более изображений, наложенных друг на друга, CAShapeLayer в масках UIImageView медленно перемещается на мое прикосновение.

Вот несколько примеров кода:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"SomeImage.jpg" ofType:nil]]; 
CGMutablePathRef path = CGPathCreateMutable(); 
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25)); 

for (int i = 0; i < 200; i++) { 

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:image]; 
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), image.size.width * .25, image.size.height * .25); 

    CAShapeLayer *shape = [CAShapeLayer layer]; 
    shape.path = path; 
    imageView.layer.mask = shape; 

    [self.view addSubview:imageView]; 
    [imageView release]; 

} 
CGPathRelease(path); 

С выше коде, imageView очень лага. Тем не менее, он реагирует мгновенно, если я маскировать его вручную в bitmap context:

UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle]pathForResource:@"3.0-Pad-Classic0.jpg" ofType:nil]]; 
CGMutablePathRef path = CGPathCreateMutable(); 
CGPathAddEllipseInRect(path, NULL, CGRectMake(0.f, 0.f, image.size.width * .25, image.size.height * .25)); 


for (int i = 0; i < 200; i++) { 

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width * .25, image.size.height * .25), NO, [[UIScreen mainScreen]scale]); 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    CGContextAddPath(ctx, path); 
    CGContextClip(ctx); 

    [image drawInRect:CGRectMake(-(image.size.width * .25), -(image.size.height * .25), image.size.width, image.size.height)]; 
    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    SLTUIImageView *imageView = [[SLTUIImageView alloc]initWithImage:finalImage]; 
    imageView.frame = CGRectMake(arc4random_uniform(CGRectGetWidth(self.view.bounds)), arc4random_uniform(CGRectGetHeight(self.view.bounds)), finalImage.size.width, finalImage.size.height); 

    [self.view addSubview:imageView]; 
    [imageView release]; 

} 
CGPathRelease(path); 

Кстати, вот код SLTUIImageView, это всего лишь простой подкласс UIImageView, который реагирует на прикосновения (для тех, кто задавался вопросом) :

-(id)initWithImage:(UIImage *)image{ 

    self = [super initWithImage:image]; 
    if (self) { 
     self.userInteractionEnabled = YES; 
    } 
    return self; 
} 

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ 
    [self.superview bringSubviewToFront:self]; 
} 

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ 

    UITouch *touch = [touches anyObject]; 
    self.center = [touch locationInView:self.superview]; 
} 

можно ли как-то оптимизировать, как CAShapeLayer маскирует UIImageView так, что улучшается производительность? Я попытался выяснить, где бутылочная шее использует Time Profiler в Instruments, но я не могу точно сказать, что именно вызывает его.

Я попытался установить shouldRasterize на YES на обоих layer и на layer.mask, но ни один из них не имеет никакого эффекта. Я не уверен, что делать.

Edit:

Я сделал больше тестов и обнаружили, что, если я использую только регулярные CALayer маскировать другую CALayer (layer.mask = someOtherLayer) У меня есть одни и те же проблемы с производительностью. Похоже, что проблема не является специфической для CAShapeLayer -пратной спецификой для свойства maskCALayer.

Edit 2:

Таким образом, после получения дополнительной информации о использовании Core Animation tool в Instruments, я узнал, что представление визуализируется закадровый каждый раз, когда она движется. Установка shouldRaster на YES, когда прикосновение начинается и выключается, когда касание заканчивается, изображение остается зеленым (сохраняя кэш) в instruments, но производительность все еще ужасна. Я считаю, что это связано с тем, что, хотя представление кэшируется, если оно не непрозрачно, оно должно быть повторно отображено с каждым фреймом.

Одна вещь, которая должна подчеркнуть, заключается в том, что если в масках имеется всего несколько видов (скажем, даже около десяти), производительность довольно хорошая. Однако, когда вы увеличиваете это до 100 or more, производительность отстает. Я предполагаю, что это происходит потому, что, когда кто-то перемещается над другими, все они должны быть повторно отображены.

Мое заключение таково, У меня есть один из двух вариантов.

Во-первых, должно быть какое-то время, чтобы надолго замаскировать представление (визуализировать его один раз и называть его хорошим).Я знаю, что это можно сделать с помощью графического или растрового контекстного маршрута, как показано в моем примере кода, но когда слой маскирует его представление, это происходит мгновенно. Когда я делаю это в растровом контексте, как показано, он довольно медленный (так как в нем почти невозможно даже сравнить, насколько он медленнее).

Во-вторых, должен быть способ faster, чтобы сделать это через маршрут контекста битмапа. Если есть эксперт в маскировке изображений или представлений, их помощь будет очень оценена.

+0

Ваш лучший выбор - это сжать ваши 200 изображений, добавленных вами в цикл for, в 1 большом изображении, используя код, упомянутый здесь: http://stackoverflow.com/questions/4334233/how-to-capture- uiview-to-uiimage-without-loss-of-quality-on-retina-display Вы также можете попробовать использовать инструменты и использовать профилировщик времени, чтобы проверить, где находится узкое место вашего кода. –

+0

Вы пробовали делать изображения раньше времени в их окончательное состояние, прежде чем использовать их в приложении, чтобы приложение не нужно было выполнять всю эту графическую обработку, особенно если у вас так много изображений для выполнения этой обработки и их перемещают, поэтому им приходится постоянно повторно отображать их все. Я не уверен, что это жизнеспособный вариант для вас, и если вы еще это пробовали. Также сложно понять, что вы пытаетесь сделать с изображениями без картинки, в этом случае важно, если вы приложите скриншот, который это поможет. –

+1

Это связано с тем, что вы уже указали на экранную визуализацию. Я не думаю, что вам удастся оптимизировать производительность слоя, но, как вы уже видели, вы можете оптимизировать этот процесс. Еще одним шагом может стать подкласс, UIView переопределяет drawRect для достижения того, что вам нужно. Вызов drawRect вызывается только в том случае, если ваше представление отмечено как грязное. Не подкласса UIImageView bacuse drawRect никогда не вызывается. – Andrea

ответ

1

Вы прошли довольно далеко, и я считаю, что это почти решение. Я бы сделал просто расширение того, что вы уже пробовали. Поскольку вы говорите, что многие из этих слоев «заканчиваются» в конечных положениях, которые остаются постоянными относительно других слоев и маски. Поэтому просто визуализируйте все эти «готовые» слои в один растровый контекст. Таким образом, каждый раз, когда вы выписываете слой в этот единственный контекст, у вас будет один меньше слоев, чтобы беспокоиться об этом, замедляя процесс анимации/рендеринга.

0

Кварц (drawRect :) медленнее CoreAnimation по многим причинам: CALayer vs CGContextdrawRect vs CALayer. Но нужно использовать его правильно.

В документации вы можете увидеть некоторые советы. ImprovingCoreAnimationPerformance

Если вам нужна высокая производительность, возможно, вы можете попробовать использовать AsyncDisplayKit. Эта структура позволяет создавать плавные и гибкие приложения.

Смежные вопросы