Округлые углы на UIImage

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

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

- (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)

Ответ 1

Вот еще более простой метод, доступный в 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]];

Ответ 2

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

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

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

Ответ 3

Если 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>

Ответ 4

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

Мое обходное решение прошло примерно так:

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

(В моем случае я изменял/масштабировал UIImages.)

Ответ 8

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

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

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

В Swift 4.2 и 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
}

Ответ 12

Установите изображение в 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;
    }