Скругленные углы на UIImage - PullRequest
       61

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

57 голосов
/ 15 октября 2008

Я пытаюсь рисовать изображения на 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)

Ответы [ 12 ]

163 голосов
/ 20 августа 2009

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

Я пойду вперед и на самом деле отвечу на вопрос в названии.

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

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
21 голосов
/ 23 сентября 2009

Если 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 голосов
/ 17 января 2009

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

4 голосов
/ 15 октября 2008

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

3 голосов
/ 17 декабря 2008

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

2 голосов
/ 04 февраля 2009

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

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

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

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

2 голосов
/ 04 февраля 2009

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

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

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

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

1 голос
/ 15 октября 2008

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

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

0 голосов
/ 01 февраля 2019

В 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
}
...