Рисование поэтапно в UIView (iPhone)

Насколько я понял до сих пор, каждый раз, когда я рисую что-то в drawRect: UIView, весь контекст стирается, а затем перерисовывается.

Поэтому я должен сделать что-то подобное, чтобы нарисовать серию точек:

Метод A: рисование всего на каждом вызове

- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, self.bounds, maskRef); //draw the mask CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode for (Drop *drop in myPoints) { CGContextAddEllipseInRect(context, CGRectMake(drop.point.x - drop.size/2, drop.point.y - drop.size/2, drop.size, drop.size)); } CGContextSetRGBFillColor(context, 0.5, 0.0, 0.0, 0.8); CGContextFillPath(context); } 

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

EDIT: Используя код MrMage, я сделал следующее, которое, к сожалению, так же медленное, и смешение цветов не работает. Любой другой метод, который я мог бы попробовать?

Метод B: сохранение предыдущих рисунков в UIImage и только рисование нового материала, и это изображение

 - (void)drawRect:(CGRect)rect { //draw on top of the previous stuff UIGraphicsBeginImageContext(self.frame.size); CGContextRef ctx = UIGraphicsGetCurrentContext(); // ctx is now the image's context [cachedImage drawAtPoint:CGPointZero]; if ([myPoints count] > 0) { Drop *drop = [myPoints objectAtIndex:[myPoints count]-1]; CGContextClipToMask(ctx, self.bounds, maskRef); //respect alpha mask CGContextAddEllipseInRect(ctx, CGRectMake(drop.point.x - drop.dropSize/2, drop.point.y - drop.dropSize/2, drop.dropSize, drop.dropSize)); CGContextSetRGBFillColor(ctx, 0.5, 0.0, 0.0, 1.0); CGContextFillPath(ctx); } [cachedImage release]; cachedImage = [UIGraphicsGetImageFromCurrentImageContext() retain]; UIGraphicsEndImageContext(); //draw on the current context CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, self.bounds, maskRef); //draw the mask CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode [cachedImage drawAtPoint:CGPointZero]; //draw the cached image } 

EDIT: В конце концов я объединил один из методов, упомянутых ниже, с перерисовкой только в новом прямоугольнике. Результат: БЫСТРЫЙ МЕТОД:

 - (void)addDotAt:(CGPoint)point { if ([myPoints count]  0) { Drop *drop = [myPoints objectAtIndex:[myPoints count]-1]; CGPathAddEllipseInRect (dotsPath, NULL, CGRectMake(drop.point.x - drop.dropSize/2, drop.point.y - drop.dropSize/2, drop.dropSize, drop.dropSize)); } CGContextAddPath(context, dotsPath); CGContextSetRGBFillColor(context, 0.5, 0.0, 0.0, 1.0); CGContextFillPath(context); } 

Всем спасибо!

Если вы только на самом деле меняете небольшую часть содержимого UIView каждый раз, когда вы рисуете (а остальная часть контента обычно остается той же), вы можете использовать это. Вместо того, чтобы перерисовывать все содержимое UIView каждый раз, вы можете отметить только области представления, которые требуют перерисовки, используя -[UIView setNeedsDisplayInRect:] вместо -[UIView setNeedsDisplay] . Вам также необходимо убедиться, что графический контент не очищен до рисования, установив view.clearsContextBeforeDrawing = YES;

Конечно, все это также означает, что ваша drawRect: реализация должна уважать параметр rect, который затем должен быть небольшим подразделением прямоугольника вашего полного вида (если только что-то не загрязняет весь прямоугольник) и только рисует в этой части.

Вы можете сохранить свой CGPath в качестве члена вашего classа. И используйте это в методе рисования, вам нужно будет создать путь, когда точки будут меняться, но не каждый раз, когда изображение перерисовывается, если точки являются инкрементальными, просто добавляйте эллипсы к пути. В методе drawRect вам нужно будет только добавить путь

 CGContextAddPath(context,dotsPath); -(CGMutablePathRef)createPath { CGMutablePathRef dotsPath = CGPathCreateMutable(); for (Drop *drop in myPoints) { CGPathAddEllipseInRect ( dotsPath,NULL, CGRectMake(drop.point.x - drop.size/2, drop.point.y - drop.size/2, drop.size, drop.size)); } return dotsPath; } 

Если я правильно понимаю вашу проблему, я бы попытался напрямую рисовать CGBitmapContext вместо экрана. Затем в drawRect нарисуйте только часть предварительно обработанного растрового изображения, которое необходимо из параметра rect .

Сколько эллипсов вы собираетесь нарисовать? В общем, Core Graphics должна быть способна быстро нарисовать много эллипсов.

Однако вы можете кэшировать свои старые рисунки на изображение (я не знаю, действительно ли это решение более эффективно):

 UIGraphicsBeginImageContext(self.frame.size); CGContextRef ctx = UIGraphicsGetCurrentContext(); // ctx is now the image's context [cachedImage drawAtPoint:CGPointZero]; // only plot new ellipses here... [cachedImage release]; cachedImage = [UIGraphicsGetImageFromCurrentImageContext() retain]; UIGraphicsEndImageContext(); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, self.bounds, maskRef); //draw the mask CGContextClipToMask(context, self.bounds, maskRef); //respect alpha mask CGContextSetBlendMode(context, kCGBlendModeColorBurn); //set blending mode [cachedImage drawAtPoint:CGPointZero]; 

Если вы можете кэшировать чертеж в качестве изображения, вы можете воспользоваться поддержкой CoreAnimation от UIView. Это будет намного быстрее, чем использование Quartz, так как Quartz делает свой рисунок в программном обеспечении.

 - (CGImageRef)cachedImage { /// Draw to an image, return that } - (void)refreshCache { myView.layer.contents = [self cachedImage]; } - (void)actionThatChangesWhatNeedsToBeDrawn { [self refreshCache]; } 
Давайте будем гением компьютера.