2 votes

CGContextSaveGState 0x0 dans UIBezierPath

Je suis en train d'essayer d'utiliser la méthode d'Apples pour détecter si un point se trouve sur un UIBezierPath. Cependant, cela renvoie un contexte invalide.

Comme vous pouvez le voir sur le NSlog, je passe un UIBezierPath et un point à vérifier. Dans mon cas, un point de toucher.

Je ne comprends pas pourquoi. Quelqu'un peut-il m'expliquer ou me diriger dans la bonne direction ?

NSLOG -----

Path 
Contains point Path 
Point de toucher 425.000000 139.000000
: CGContextSaveGState: contexte invalide 0x0
: CGContextAddPath: contexte invalide 0x0
: CGContextPathContainsPoint: contexte invalide 0x0
: CGContextRestoreGState: contexte invalide 0x0
NON

Tiré directement de la documentation d'Apple sur comment déterminer un point dans un chemin

- (BOOL)containsPoint:(CGPoint)point onPath:(UIBezierPath *)path inFillArea:(BOOL)inFill {

    NSLog(@"contains point Path %@", path);
    NSLog(@"Point de toucher %f %f", point.x, point.y );

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGPathRef cgPath = path.CGPath;
    BOOL    isHit = NO;
// Déterminer le mode de dessin à utiliser. Par défaut, détecter les hits sur la partie tracée du chemin.
    CGPathDrawingMode mode = kCGPathStroke;

    if (inFill) { // Recherche de hits dans la zone de remplissage du chemin.
        if (path.usesEvenOddFillRule)
            mode = kCGPathEOFill;
        else
            mode = kCGPathFill;
    }
 // Sauvegarder l'état graphique pour que le chemin puisse être retiré plus tard.
    CGContextSaveGState(context);
    CGContextAddPath(context, cgPath);

    // Faire la détection de hit.
    isHit = CGContextPathContainsPoint(context, point, mode);

    CGContextRestoreGState(context);

    return isHit;
}

Voici ma méthode touchesBegan. J'ai mes chemins dans un NSMutableArray. Je parcours le tableau pour vérifier tous mes chemins pour voir si l'un a été touché.

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

 CGPoint curPoint = [[touches anyObject] locationInView:self];
  for (int i = 0; i < [pathInfo count]; i++){
            NSArray *row = [[NSArray alloc] initWithArray:[pathInfo objectAtIndex:i]];
            UIBezierPath *path = [row objectAtIndex:0];

            NSLog(@"Chemin %@", path);
if ([self containsPoint:curPoint onPath:path inFillArea:NO]){
               NSLog(@"OUI");
            } else {
                NSLog("NON");
            }

        }
}

4voto

David Rönnqvist Points 25290

tl;dr: Vous devriez utiliser CGPathContainsPoint( ... ) à la place.


Ce qui s'est mal passé

Votre problème est que vous n'avez aucun contexte où vous essayez de l'obtenir

CGContextRef context = UIGraphicsGetCurrentContext(); // <-- Cette ligne ici...

La méthode UIGraphicsGetCurrentContext ne renverra un contexte que s'il existe un contexte actuel valide. Les deux exemples principaux sont

  1. À l'intérieur de drawRect: (où le contexte est la vue dans laquelle vous dessinez)

  2. À l'intérieur de votre propre contexte d'image (lorsque vous utilisez UIGraphicsBeginImageContext() pour pouvoir utiliser Core Graphics pour dessiner dans une image (peut-être que vous le passez à une autre partie de votre code et l'affichez dans une image ou l'enregistrez sur le disque)).

La solution

Je ne sais pas pourquoi vous faisiez tout ce travail supplémentaire de contextes, de sauvegarde et de restauration de l'état, etc. Il semble que vous avez manqué la méthode simple CGPathContainsPoint().

BOOL isHit = CGPathContainsPoint(
                                 path.CGPath,
                                 NULL,
                                 point,
                                 path.usesEvenOddFillRule
                                 );

Éditer

Si vous souhaitez tester un chemin de trait, vous pourriez utiliser CGPathCreateCopyByStrokingPath() pour d'abord créer un nouveau chemin rempli du chemin que vous tracez (avec une certaine largeur, etc.). Ole Begemann a une très bonne explication sur son blog sur comment le faire (y compris quelques exemples de code).

4voto

rob mayoff Points 124153

La méthode CGContextPathContainsPoint nécessite un contexte graphique, que le code source d'Apple obtient à partir de UIGraphicsGetCurrentContext. Cependant, UIGraphicsGetCurrentContext ne fonctionne qu'à l'intérieur de -[UIView drawRect:] ou après un appel à une fonction qui définit un contexte graphique UI, comme UIGraphicsBeginImageContext.

Vous pouvez effectuer votre test de détection sans un contexte graphique en utilisant CGPathCreateCopyByStrokingPath (ajouté dans iOS 5.0) et CGPathContainsPoint sur la copie traitée :

static BOOL strokedPathContainsPoint(CGPathRef unstrokedPath,
    const CGAffineTransform *transform, CGFloat lineWidth,
    CGLineCap lineCap, CGLineJoin lineJoin, CGFloat miterLimit,
    CGPoint point, bool eoFill)
{
    CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(unstrokedPath,
        transform, lineWidth, lineCap, lineJoin, miterLimit);
    BOOL doesContain = CGPathContainsPoint(strokedPath, NULL, point, eoFill);
    CGPathRelease(strokedPath);
    return doesContain;
}

Vous devez décider de l'épaisseur de ligne et des autres paramètres de traitement que vous souhaitez utiliser. Par exemple :

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    CGPoint curPoint = [[touches anyObject] locationInView:self];
    for (int i = 0; i < [pathInfo count]; i++){
        NSArray *row = [[NSArray alloc] initWithArray:[pathInfo objectAtIndex:i]];
        UIBezierPath *path = [row objectAtIndex:0];

        NSLog(@"Chemin %@", path);
        if (strokedPathContainsPoint(path.CGPath, NULL, 10.0f, kCGLineCapRound,
            kCGLineJoinRound, 0, curPoint, path.usesEvenOddFillRule))
        {
            NSLog(@"OUI");
        } else {
            NSLog("NON");
        }
    }
}

Notez que CGPathCreateCopyByStrokingPath est probablement assez coûteux, vous voudrez peut-être traiter vos chemins une fois, et sauvegarder les copies traitées, au lieu de les traiter à chaque fois que vous devez tester un point.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X