2008-10-15 2 views
57

Я пытаюсь рисовать изображения на iPhone, используя закругленные углы, а также контактные изображения в приложении «Контакты». У меня есть код, который обычно работает, но он иногда вылетает внутри подпрограмм рисования UIImage с помощью EXEC_BAD_ACCESS - KERN_INVALID_ADDRESS. Я думал, что это может быть связано с cropping question Я спросил несколько недель назад, но я считаю, что правильно настраиваю путь отсечения.Округлые углы на UIImage

Вот код, который я использую - когда он не падает, результат выглядит отлично, и любой, кто хочет получить подобный вид, может свободно заимствовать код.

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

и вот журнал аварий. Она выглядит так же, всякий раз, когда я получаю один из этих аварий

 
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) 
+0

Почему ты CGContextSaveGState() и CGContextRestoreGState()? Читая документацию, создается впечатление, что состояние является внутренним для контекста, и весь контекст все равно отбрасывается. – 2009-07-02 13:53:18

+0

Нужно ли вызывать CGContextBeginPath() и CGContextClosePath(), как вы это делаете? Я обнаружил, что могу просто создать путь и вызвать CGContextAddPath(), и все работает нормально. Кроме того, в документах для CGContextClosePath() говорится, что «когда вы заполняете или кликаете открытый путь, Quartz неявно закрывает для вас подпуть», предполагая, что его не нужно закрывать самостоятельно. – camdez 2009-10-23 23:04:42

ответ

163

Вот еще более простой способ, который доступен в iPhone 3.0 и выше. Каждый объект, основанный на представлении, имеет связанный уровень. Каждый слой может иметь набор угловой радиус, это даст вам только то, что вы хотите:

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

Я не могу предложить какое-либо представление о вашей аварию, но я думал, что я хотел бы предложить еще один вариант для округления углов. У меня возникла аналогичная проблема в приложении, над которым я работал. Вместо того, чтобы писать какой-либо код, я накладываю другое изображение, которое маскирует углы.

+0

Я пробовал маскировку с изображением и никогда не мог заставить его работать надежно/вообще. У вас есть указатели на то, как вы получили его на работу? Это было давно, поэтому я точно не помню, что я пробовал, но я думаю, что прошел через CGImageCreateWithMask, CGImageMaskCreate и CGContextClipToMask. – Jablair 2008-10-15 19:03:03

+0

Я не маскирую код. Я просто рисую еще один UIImage поверх изображения, которое хочу замаскировать. UIImage имеет прозрачную секцию в середине и черном (моем цвете фона), которые я хочу «замаскировать». – Lounges 2008-10-16 18:11:41

1

Если он только немного сработает, выясните, что имеют случаи аварии. Является ли dstRect одинаковым каждый раз? Являются ли образы когда-либо разных размеров?

Кроме того, вам необходимо CGPathRelease(borderPath), хотя я сомневаюсь, что утечка вызывает вашу проблему.

+0

Спасибо за напоминание о выпуске. Я случайно прокомментировал это с помощью другого кода отладки, который я затем подрезал перед публикацией. dstRect - это то же самое, но изображения могут быть разными, потому что они загружаются из Интернета. Я получаю их от тестера - я не перепродал это сам. – Jablair 2008-10-15 20:26:27

3

Самый простой способ - вставить отключенную кнопку [!] Круглой прямоугольной кнопки [нестандартная!] В вашем представлении (может даже сделать все это в построителе интерфейса) и связать с ней изображение. Сообщение об установке изображения отличается для UIButton (по сравнению с UIImageView), но общий kludge работает как шарм. Используйте setImage: forState: если вы хотите по центру значок или setBackgroundImage: forState: если вы хотите, чтобы все изображение с углами разрезалось (например, Контакты). Конечно, если вы хотите отображать много этих изображений в своем drawRect, это не правильный подход, но, скорее всего, встроенный просмотр - это именно то, что вам нужно ...

4

Если вы вызываете свой метод (borderedImageWithRect) в фоновом потоке могут возникнуть сбои, поскольку функции UIGraphics не являются потокобезопасными. В таком случае вы должны создать контекст с использованием CGBitmapContextCreate() - см. Пример кода «Reflection» из SDK.

+0

fjoachim, вы на самом деле это сделали? У меня проблемы с рисованием в контексте растрового изображения в фоновом потоке. См. Http://stackoverflow.com/questions/702914/using-core-graphics-cocoa-can-you-draw-to-a-bitmap-context-from-a-background-th – philsquared 2009-04-01 12:59:52

2

Я бы повторил fjoachim's answer: будьте осторожны при попытке нарисовать во время работы по отдельной теме, или вы можете получить EXC_BAD_ACCESS ошибок.

Мой обходной путь пошел что-то вроде этого: (. В моем случае я изменение размеров/масштабирование UIImages)

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

2

я на самом деле имел возможность поговорить об этом с кем-то из компании Apple на iPhone Tech Talk в Нью-Йорке. Когда мы поговорили об этом, он был уверен, что это не прошивка. Вместо этого он думал, что мне нужно сохранить графический контекст, который был сгенерирован при вызове UIGraphicsBeginImageContext. Это, по-видимому, нарушает общие правила, касающиеся правил сохранения и схем именования, но этот парень был уверен, что раньше видел эту проблему.

Если память была нацарапана, возможно, другой нитью, это, безусловно, объяснило бы, почему я иногда видел проблему.

У меня не было времени пересмотреть код и проверить исправление, но комментарий PCheese заставил меня понять, что я не размещал информацию здесь.

... если я не писал, что вниз неправильно и UIGraphicsBeginImageContext должен был CGBitmapContextCreate ...

21

Если appIconImage является UIImageView, то:

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

А также помните:

#import <QuartzCore/QuartzCore.h> 
24

Я собираюсь пойти здесь и ответить на вопрос в названии.

Пробуйте эту категорию.

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 
-1

Установите изображение в XIb или раскадровки (ширины изображения и высоты 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; 
    } 
0
UIImage *originalImage = [UIImage imageNamed:@"OriginalImage.png"] 
[self performSelectorOnMainThread:@selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];