Рисование иерархии представлений в определенном контексте в Cocoa
-
08-06-2019 - |
Вопрос
Для части моего приложения мне необходимо создать изображение определенного вида и всех его подвидов.
Чтобы сделать это, я создаю контекст, который обертывает растровое изображение того же размера, что и представление, но я не уверен, как нарисовать в нем иерархию представлений.Я могу нарисовать одно представление, просто установив контекст и явно вызвав drawRect , но это касается не всех подвидов.
Я не вижу ничего в интерфейсе NSView, что могло бы помочь в этом, поэтому я подозреваю, что решение может находиться на более высоком уровне.
Решение
Я обнаружил, что самостоятельное написание кода для рисования было лучшим способом:
- устраните потенциальные проблемы с прозрачностью (некоторые другие опции действительно добавляют белый фон ко всему изображению).
- производительность была намного лучше
Приведенный ниже код не идеален, потому что он не решает проблемы масштабирования при переходе от границ к фреймам, но он учитывает состояние isFlipped и очень хорошо работает для того, для чего я его использовал.Обратите внимание, что он рисует только подвиды (и подсубвидения,...рекурсивно), но заставить его также рисовать самого себя очень просто, просто добавьте [self drawRect:[self bounds]]
при осуществлении imageWithSubviews
.
- (void)drawSubviews
{
BOOL flipped = [self isFlipped];
for ( NSView *subview in [self subviews] ) {
// changes the coordinate system so that the local coordinates of the subview (bounds) become the coordinates of the superview (frame)
// the transform assumes bounds and frame have the same size, and bounds origin is (0,0)
// handling of 'isFlipped' also probably unreliable
NSAffineTransform *transform = [NSAffineTransform transform];
if ( flipped ) {
[transform translateXBy:subview.frame.origin.x yBy:NSMaxY(subview.frame)];
[transform scaleXBy:+1.0 yBy:-1.0];
} else
[transform translateXBy:subview.frame.origin.x yBy:subview.frame.origin.y];
[transform concat];
// recursively draw the subview and sub-subviews
[subview drawRect:[subview bounds]];
[subview drawSubviews];
// reset the transform to get back a clean graphic contexts for the rest of the drawing
[transform invert];
[transform concat];
}
}
- (NSImage *)imageWithSubviews
{
NSImage *image = [[[NSImage alloc] initWithSize:[self bounds].size] autorelease];
[image lockFocus];
// it seems NSImage cannot use flipped coordinates the way NSView does (the method 'setFlipped:' does not seem to help)
// Use instead an NSAffineTransform
if ( [self isFlipped] ) {
NSAffineTransform *transform = [NSAffineTransform transform];
[transform translateXBy:0 yBy:NSMaxY(self.bounds)];
[transform scaleXBy:+1.0 yBy:-1.0];
[transform concat];
}
[self drawSubviews];
[image unlockFocus];
return image;
}
Другие советы
Вы можете использовать -[NSView dataWithPDFInsideRect:]
чтобы отобразить всю иерархию представления, в которое вы его отправляете, в формате PDF, возвращаемом в виде NSData
объект.Затем вы можете делать с этим все, что пожелаете, включая рендеринг в виде растрового изображения.
Однако вы уверены, что вам нужно растровое представление?В конце концов, этот PDF-файл может быть (по крайней мере, теоретически) независимым от разрешения.
Вы можете использовать -[NSBitmapImageRep initWithFocusedViewRect:]
после блокировки сфокусируйтесь на представлении, чтобы оно отображало само себя (и свои подвиды) в заданном прямоугольнике.
То, что вы хотите сделать, уже доступно явно.Смотрите раздел "NSView Drawing Redirection API" в примечаниях к выпуску 10.4 AppKit.
Создайте NSBitmapImageRep для кэширования и очистите его:
NSGraphicsContext *bitmapGraphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:cacheBitmapImageRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:bitmapGraphicsContext];
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, [cacheBitmapImageRep size].width, [cacheBitmapImageRep size].height));
[NSGraphicsContext restoreGraphicsState];
Кэшировать к нему:
-[NSView cacheDisplayInRect:toBitmapImageRep:]
Если вы хотите более широко использовать указанный контекст, корректно обрабатывая рекурсию представления и прозрачность,
-[NSView displayRectIgnoringOpacity:inContext:]
Да, для данного конкретного использования немасштабированные данные изображения - это именно то, что мне нужно.