Cantos arredondados na UIImage

Estou tentando desenhar imagens no iPhone usando cantos arredondados, como as imagens de contato no aplicativo Contatos. Eu tenho código que geralmente funcionam, mas ocasionalmente falha dentro das rotinas de desenho UIImage com um EXEC_BAD_ACCESSKERN_INVALID_ADDRESS . Eu pensei que isso poderia estar relacionado com a pergunta de corte que eu perguntei algumas semanas atrás, mas acredito que estou configurando o caminho de recorte corretamente.

Aqui está o código que estou usando – quando ele não trava, o resultado parece bom e qualquer um que procure obter uma aparência semelhante está livre para pegar o código emprestado.

 - (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; } 

e aqui está o log de falhas. Parece o mesmo sempre que recebo uma dessas falhas

 Tipo de exceção: EXC_BAD_ACCESS (SIGSEGV)
 Códigos de exceção: KERN_INVALID_ADDRESS em 0x6e2e6181
 Tópico Crashed: 0

 Tópico 0 Crashed:
 0 com.apple.CoreGraphics 0x30fe56d8 CGGStateGetRenderingIntent + 4
 1 libRIP.A.dylib 0x33c4a7d8 ripc_RenderImage + 104
 2 libRIP.A.dylib 0x33c51868 ripc_DrawImage + 3860
 3 com.apple.CoreGraphics 0x30fecad4 CGContextDelegateDrawImage + 80
 4 com.apple.CoreGraphics 0x30feca40 CGContextDrawImage + 368
 5 UIKit 0x30a6a708 - [UIImage drawInRect: blendMode: alpha:] + 1460
 6 UIKit 0x30a66904 - [UIImage drawInRect:] + 72
 7 MyApp 0x0003f8a8 - [UIImage (PNAdditions) borderedImageWithRect: raio:] (UIImage + PNAdditions.m: 187)

Aqui está um método ainda mais fácil que está disponível no iPhone 3.0 e acima. Todo object baseado em vista possui uma camada associada. Cada camada pode ter um raio de canto definido, isso lhe dará exatamente o que você deseja:

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

Eu vou em frente aqui e realmente responder a pergunta no título.

Experimente esta categoria.

UIImage + additions.h

 #import  @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 

Se appIconImage for um UIImageView, então:

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

E lembre-se também:

 #import  

Eu não posso oferecer qualquer insight sobre o seu acidente, mas eu pensei em oferecer outra opção para arredondar os cantos. Eu tive um problema semelhante surgir em um aplicativo que eu estava trabalhando. Em vez de escrever qualquer código, estou cobrindo outra imagem que mascara os cantos.

Se você estiver chamando seu método (borderedImageWithRect) em um encadeamento de segundo plano, poderão ocorrer falhas, uma vez que as funções do UIGraphics não são seguras para encadeamento. Nesse caso, você deve criar um contexto usando CGBitmapContextCreate () – consulte o código de exemplo “Reflection” do SDK.

A maneira mais fácil é incorporar um botão [!] Redondo-recto [não personalizado!] Desactivado na sua opinião (pode até mesmo fazer tudo no Interface Builder) e associar a sua imagem a ele. A mensagem de definição de imagem é diferente para o UIButton (comparado ao UIImageView), mas o kludge geral funciona como um encanto. Use setImage: forState: se você quiser um ícone centrado ou setBackgroundImage: forState: se você quiser a imagem inteira com cantos cortados (como Contatos). É claro que se você quiser exibir muitas dessas imagens em seu drawRect, essa não é a abordagem correta, mas é mais provável que uma visualização incorporada seja exatamente o que você precisava …

Gostaria de reiterar a resposta do fjoachim : seja cauteloso ao tentar desenhar durante a execução de um thread separado, ou você pode obter erros EXC_BAD_ACCESS .

Minha solução foi algo assim:

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

(No meu caso eu estava redimensionando / escalando UIImagens.)

Eu realmente tive a chance de falar sobre isso com alguém da Apple no iPhone Tech Talk em Nova York. Quando falamos sobre isso, ele tinha certeza de que não era um encadeamento emitido. Em vez disso, ele pensou que eu precisava manter o contexto gráfico que foi gerado ao chamar UIGraphicsBeginImageContext . Isso parece violar as regras gerais que lidam com regras de manutenção e esquemas de nomenclatura, mas esse sujeito tinha certeza de que já havia visto o problema anteriormente.

Se a memory estivesse sendo rabiscada, talvez por outro tópico, isso certamente explicaria por que eu estava apenas vendo o problema ocasionalmente.

Eu não tive tempo para revisitar o código e testar a correção, mas o comentário de PCheese me fez perceber que eu não tinha postado a informação aqui.

… a menos que eu escrevesse errado e que UIGraphicsBeginImageContext deveria ter sido CGBitmapContextCreate

Se ele falhar apenas o tempo todo, descubra o que os casos de acidentes têm em comum. O dstRect é o mesmo todas as vezes? As imagens são de tamanho diferente?

Além disso, você precisa de CGPathRelease(borderPath) , embora eu duvido que o vazamento esteja causando o seu problema.

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

Defina a imagem no xib ou no storyboard (largura e altura da imagem 41×41).

FirstViewController.h

 @interface.... IBOutlet UIImageView *testImg; @end 

FirstViewController.m

 -(void)viewDidLoad{ testImg.layer.backgroundColor=[[UIColor clearColor] CGColor]; testImg.layer.cornerRadius=20; testImg.layer.masksToBounds = YES; }