iphone - español - Esquinas redondeadas en UIImage
cocoa touch español (11)
Intento dibujar imágenes en el iPhone usando esquinas redondeadas, al igual que las imágenes de contacto en la aplicación Contactos. Tengo un código que generalmente funciona, pero ocasionalmente se cuelga dentro de las rutinas de dibujo de UIImage con un EXEC_BAD_ACCESS
- KERN_INVALID_ADDRESS
. Pensé que esto podría estar relacionado con la pregunta de recorte que hice hace unas semanas, pero creo que estoy configurando el trazado de recorte correctamente.
Aquí está el código que estoy usando: cuando no falla, el resultado se ve bien y cualquiera que busque obtener un aspecto similar puede tomar prestado el código.
- (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;
}
y aquí está el registro de bloqueo. Se ve igual siempre que tengo uno de estos bloqueos
Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x6e2e6181 Crashed Thread: 0 Thread 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:radius:] (UIImage+PNAdditions.m:187)
Aquí hay un método aún más fácil que está disponible en iPhone 3.0 y posteriores. Cada objeto basado en la vista tiene una capa asociada. Cada capa puede tener un radio de esquina establecido, esto le dará exactamente lo que quiere:
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]];
De hecho, tuve la oportunidad de hablar de esto con alguien de Apple en el iPhone Tech Talk en Nueva York. Cuando hablamos de eso, él estaba bastante seguro de que no era un subproceso emitido. En cambio, pensó que necesitaba retener el contexto gráfico que se generó al llamar a UIGraphicsBeginImageContext
. Esto parece violar las reglas generales que tratan de retener reglas y esquemas de nombres, pero este tipo estaba bastante seguro de haber visto el problema anteriormente.
Si la memoria estuviera siendo garabateada, quizás por otro hilo, eso ciertamente explicaría por qué solo estaba viendo el problema de vez en cuando.
No he tenido tiempo de revisar el código y probar la solución, pero el comentario de PCheese me hizo darme cuenta de que no había publicado la información aquí.
... a menos que lo haya escrito mal y UIGraphicsBeginImageContext
debería haber sido CGBitmapContextCreate
...
Establezca la imagen en xib o guión gráfico (ancho y alto de la imagen 41x41).
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;
}
La manera más fácil es insertar un botón deshabilitado [!] Redondo-recto [no personalizado!] En su vista (incluso puede hacerlo todo en el Creador de Interfaces) y asociar su imagen con él. El mensaje de configuración de imagen es diferente para UIButton (en comparación con UIImageView), pero el kludge general funciona como un amuleto. Use setImage: forState: si desea un icono centrado o setBackgroundImage: forState: si desea que se corte toda la imagen con las esquinas (como Contactos). Por supuesto, si desea mostrar muchas de estas imágenes en su drawRect, este no es el enfoque correcto, pero es más probable que una vista incrustada sea exactamente lo que necesitaba de todos modos ...
No puedo ofrecer ninguna idea de tu caída, pero pensé que ofrecería otra opción para redondear las esquinas. Tuve un problema similar en una aplicación en la que estaba trabajando. En lugar de escribir cualquier código, estoy sobreponiendo otra imagen que enmascara las esquinas.
Reitero la respuesta de fjoachim : tenga cuidado al intentar dibujar mientras se ejecuta en un hilo separado, o puede obtener errores EXC_BAD_ACCESS
.
Mi solución fue algo como esto:
UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"]
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];
(En mi caso, estaba cambiando / escalando UIImages.)
Si appIconImage es un UIImageView, entonces:
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];
Y también recuerda:
#import <QuartzCore/QuartzCore.h>
Si está llamando a su método (borderedImageWithRect) en una cadena de fondo, pueden producirse bloqueos ya que las funciones de UIGraphics no son seguras para subprocesos. En tal caso, debe crear un contexto usando CGBitmapContextCreate () - vea el código de muestra "Reflection" del SDK.
Si solo falla un poco del tiempo, averigua qué tienen en común los casos de colisión. ¿Es dstRect el mismo todo el tiempo? ¿Alguna vez las imágenes tienen un tamaño diferente?
Además, necesita CGPathRelease(borderPath)
, aunque dudo que la fuga esté causando su problema.
Voy a seguir adelante y responder la pregunta en el título.
Prueba esta categoría
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
UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"]
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];