web-dev-qa-db-fra.com

Coins arrondis sur UIImage

J'essaie de dessiner des images sur l'iPhone en utilisant des angles arrondis, comme les images de contact dans l'application Contacts. J'ai un code qui fonctionne généralement, mais il se bloque parfois à l'intérieur des routines de dessin UIImage avec un EXEC_BAD_ACCESS - KERN_INVALID_ADDRESS. Je pensais que cela était peut-être lié à la question cropping que j'avais posée il y a quelques semaines, mais je pense avoir correctement configuré le tracé de détourage.

Voici le code que j'utilise - lorsqu'il ne tombe pas en panne, le résultat est correct et quiconque cherche à obtenir un look similaire est libre d'emprunter le code.

- (UIImage *)borderedImageWithRect: (CGRect)dstRect radius:(CGFloat)radius {
    UIImage *maskedImage = nil;

    radius = MIN(radius, .5 * MIN(CGRectGetWidth(dstRect), CGRectGetHeight(dstRect)));
    CGRect interiorRect = CGRectInset(dstRect, radius, radius);

    UIGraphicsBeginImageContext(dstRect.size);
    CGContextRef maskedContextRef = UIGraphicsGetCurrentContext();
    CGContextSaveGState(maskedContextRef);

    CGMutablePathRef borderPath = CGPathCreateMutable();
    CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(180), PNDegreeToRadian(270), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(270.0), PNDegreeToRadian(360.0), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(0.0), PNDegreeToRadian(90.0), NO);
    CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(90.0), PNDegreeToRadian(180.0), NO);

    CGContextBeginPath(maskedContextRef);
    CGContextAddPath(maskedContextRef, borderPath);
    CGContextClosePath(maskedContextRef);
    CGContextClip(maskedContextRef);

    [self drawInRect: dstRect];

    maskedImage = UIGraphicsGetImageFromCurrentImageContext();
    CGContextRestoreGState(maskedContextRef);
    UIGraphicsEndImageContext();

    return maskedImage;
}

et voici le journal des accidents. Il a le même aspect quand je reçois un de ces accidents

 Type d’exception: EXC_BAD_ACCESS (SIGSEGV) 
 Codes d’exception: KERN_INVALID_ADDRESS à 0x6e2e6181 
 Fil écrasé: 0 

 Filtrage 0 Écrasé: 
 0 com.Apple.CoreGraphics 0x30fe56d. Il se trouve dans la section suivante 368 
 5 UIKit 0x30a6a708 - [UIImage drawInRect: blendMode: alpha:] + 1460 
 6 UIKit 0x30a66904 - [UIImage drawInRect:] + 72 
 7 MonApp 0x0003f8a8 - [UIImage (PNAdditions) bordé par une image] (UIImage + PNAdditions.m: 187) 
57
Jablair

Voici une méthode encore plus simple disponible dans iPhone 3.0 et versions ultérieures. Chaque objet basé sur une vue est associé à une couche. Chaque couche peut avoir un rayon de coin défini, cela vous donnera exactement ce que vous voulez:

UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:@"wood.jpg"]];
// Get the Layer of any view
CALayer * l = [roundedView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];

// You can even add a border
[l setBorderWidth:4.0];
[l setBorderColor:[[UIColor blueColor] CGColor]];
163
MagicSeth

Je vais y aller et répondre à la question dans le titre.

Essayez cette catégorie.

UIImage + additions.h

#import <UIKit/UIKit.h>

@interface UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS;
@end

UIImage + additions.m

#import "UIImage+additions.h"

@implementation UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS {
    UIImage *image = self;

    // Begin a new image that will be the new image with the rounded corners
    // (here with the size of an UIImageView)
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);

    const CGRect RECT = CGRectMake(0, 0, image.size.width, image.size.height);
    // Add a clip before drawing anything, in the shape of an rounded rect
    [[UIBezierPath bezierPathWithRoundedRect:RECT cornerRadius:RADIUS] addClip];
    // Draw your image
    [image drawInRect:RECT];

    // Get the image, here setting the UIImageView image
    //imageView.image
    UIImage* imageNew = UIGraphicsGetImageFromCurrentImageContext();

    // Lets forget about that we were drawing
    UIGraphicsEndImageContext();

    return imageNew;
}
@end
25
Jonny

Si appIconImage est un UIImageView, alors:

appIconImage.image = [UIImage imageWithContentsOfFile:@"image.png"]; 
appIconImage.layer.masksToBounds = YES;
appIconImage.layer.cornerRadius = 10.0;
appIconImage.layer.borderWidth = 1.0;
appIconImage.layer.borderColor = [[UIColor grayColor] CGColor];

Et rappelez-vous aussi: 

#import <QuartzCore/QuartzCore.h>
21
cuasijoe

Si vous appelez votre méthode (borderedImageWithRect) dans un thread en arrière-plan, des blocages peuvent survenir car les fonctions UIGraphics ne sont pas thread-safe. Dans un tel cas, vous devez créer un contexte à l'aide de CGBitmapContextCreate () - voir l'exemple de code "Reflection" dans le SDK.

4
fjoachim

Je ne peux pas vous donner une idée de votre accident, mais je pensais pouvoir offrir une autre option pour arrondir les angles. J'ai eu un problème similaire dans une application sur laquelle je travaillais. Plutôt que d'écrire un code, je superpose une autre image qui masque les coins.

4
Lounges

Le moyen le plus simple consiste à incorporer un bouton [!] Désactivé autour de votre rectangle [non personnalisé!] Dans votre vue (vous pouvez même tout faire dans le Générateur d'interface) et à y associer votre image. Le message de paramétrage de l'image est différent pour UIButton (par rapport à UIImageView), mais le fonctionnement général fonctionne comme un charme. Utilisez setImage: forState: si vous voulez une icône centrée ou setBackgroundImage: forState: si vous voulez que toute l'image avec des coins coupés (comme Contacts). Bien sûr, si vous souhaitez afficher un grand nombre de ces images dans votre drawRect, ce n'est pas la bonne approche, mais il est plus probable qu'une vue intégrée soit exactement ce dont vous aviez besoin ...

3
Max Motovilov

Je voudrais réitérer la réponse de fjoachim : soyez prudent lorsque vous tentez de dessiner lors de l'exécution sur un thread séparé, sinon vous risquez d'obtenir des erreurs EXC_BAD_ACCESS.

Ma solution de contournement a ressemblé à ceci:

UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"] 
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];

(Dans mon cas, je redimensionnais/redimensionnais UIImages.)

2
PCheese

En fait, j’ai eu l’occasion de parler de cela avec une personne d’Apple lors de l’iPhone Tech Talk à New York. Quand nous en avons parlé, il était à peu près sûr que ce n'était pas un fil conducteur. Au lieu de cela, il pensait que je devais conserver le contexte graphique généré lors de l'appel de UIGraphicsBeginImageContext. Cela semble enfreindre les règles générales relatives aux règles de conservation et aux systèmes de nommage, mais cet homme était à peu près sûr d'avoir déjà vu le problème.

Si la mémoire était en train de gribouiller, peut-être par un autre fil, cela expliquerait certainement pourquoi je ne voyais le problème que de temps en temps.

Je n'ai pas eu le temps de revoir le code et de tester le correctif, mais le commentaire de PCheese m'a fait comprendre que je n'avais pas posté l'information ici.

... sauf si j'ai mal écrit et que UIGraphicsBeginImageContext aurait dû être CGBitmapContextCreate...

2
Jablair

Si cela ne vous bloque que de temps en temps, déterminez ce que les cas de crash ont en commun. Est-ce que dstRect est la même à chaque fois? Les images sont-elles toujours d'une taille différente?

En outre, vous devez CGPathRelease(borderPath), bien que je doute que la fuite cause votre problème.

1
benzado

Dans Swift 4.2 et Xcode 10.1

let imgView = UIImageView()
imgView.frame = CGRect(x: 200, y: 200, width: 200, height: 200)
imgView.image = UIImage(named: "yourimagename")
imgView.imgViewCorners()
//If you want complete round shape
//imgView.imgViewCorners(width: imgView.frame.width)//Pass ImageView width
view.addSubview(imgView)

extension UIImageView {
//If you want only round corners
func imgViewCorners() {
    layer.cornerRadius = 10
    layer.borderWidth = 1.0
    layer.borderColor = UIColor.red.cgColor
    layer.masksToBounds = true
}
//If you want complete round shape
func imgViewCorners(width:CGFloat) {
    layer.cornerRadius = width/2
    layer.borderWidth = 1.0
    layer.borderColor = UIColor.red.cgColor
    layer.masksToBounds = true
}
0
iOS
UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"] 
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];
0
user3536010