Question

Pour une partie de mon application, j'ai besoin de créer une image d'une certaine vue et de toutes ses sous-vues.

Pour ce faire, je crée un contexte qui enveloppe un bitmap de la même taille que la vue, mais je ne sais pas comment y intégrer la hiérarchie des vues.Je peux dessiner une seule vue en définissant simplement le contexte et en appelant explicitement drawRect, mais cela ne traite pas toutes les sous-vues.

Je ne vois rien dans l'interface NSView qui pourrait aider, donc je soupçonne que la solution se situe peut-être à un niveau supérieur.

Était-ce utile?

La solution

J'ai trouvé qu'écrire moi-même le code du dessin était le meilleur moyen de :

  • gérer les problèmes de transparence potentiels (certaines des autres options ajoutent un fond blanc à l'image entière)
  • les performances étaient bien meilleures

Le code ci-dessous n'est pas parfait, car il ne traite pas les problèmes de mise à l'échelle lors du passage des limites aux images, mais il prend en compte l'état isFlipped et fonctionne très bien pour l'utilisation pour laquelle je l'ai utilisé.Notez qu'il ne dessine que les sous-vues (et les sous-sous-vues,...récursivement), mais le faire se dessiner lui-même est très simple, il suffit d'ajouter un [self drawRect:[self bounds]] dans la mise en œuvre de 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;
}

Autres conseils

Vous pouvez utiliser -[NSView dataWithPDFInsideRect:] pour restituer toute la hiérarchie de la vue à laquelle vous l'envoyez dans un PDF, renvoyé sous forme de NSData objet.Vous pouvez ensuite faire ce que vous voulez avec cela, y compris le restituer en bitmap.

Êtes-vous sûr de vouloir une représentation bitmap ?Après tout, ce PDF pourrait être (du moins en théorie) indépendant de la résolution.

Vous pouvez utiliser -[NSBitmapImageRep initWithFocusedViewRect:] après le verrouillage, concentrez-vous sur une vue pour que la vue s'affiche elle-même (et ses sous-vues) dans le rectangle donné.

Ce que vous voulez faire est déjà disponible explicitement.Consultez la section « API de redirection de dessin NSView » dans les notes de version 10.4 d'AppKit.

Créez un NSBitmapImageRep pour la mise en cache et effacez-le :

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];

Mettez-y en cache :

-[NSView cacheDisplayInRect:toBitmapImageRep:]

Si vous souhaitez dessiner plus généralement correctement la récursion et la transparence d'une vue de gestion de contexte spécifiée,

-[NSView displayRectIgnoringOpacity:inContext:]

Oui, pour cet usage spécifique, les données d'image non mises à l'échelle sont exactement ce dont j'ai besoin.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top