Закругленные углы на 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)

Почему вы используете CGContextSaveGState () и CGContextRestoreGState ()? Читая документацию, у меня создается впечатление, что состояние является внутренним по отношению к контексту, и весь контекст в любом случае отбрасывается.

Jaka Jančar 02.07.2009 17:53

Обязательно ли вызывать CGContextBeginPath () и CGContextClosePath (), как вы? Я обнаружил, что могу просто создать путь и вызвать CGContextAddPath (), и все работает нормально. Кроме того, в документации для CGContextClosePath () говорится, что «когда вы заполняете или отсекаете открытый путь, Quartz неявно закрывает подпуть за вас», предполагая, что нет необходимости закрывать его самостоятельно.

camdez 24.10.2009 03:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
59
2
54 298
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

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

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

Jablair 15.10.2008 23:03

Я не маскируюсь в коде. Я просто рисую еще один UIImage поверх изображения, которое хочу замаскировать. UIImage имеет прозрачную секцию посередине и черные (мой цвет фона) края, которые я хочу «замаскировать».

Lounges 16.10.2008 22:11

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

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

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

Jablair 16.10.2008 00:26

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

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

Фьоахим, ты на самом деле это сделал? У меня проблема с рисованием в контексте растрового изображения в фоновом потоке. См. stackoverflow.com/questions/702914/…

philsquared 01.04.2009 16:59

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

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

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

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

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

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

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

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

Ответ принят как подходящий

Вот еще более простой способ, доступный в 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.0), но, похоже, это отличный ответ при нынешнем мировом порядке (например, 3.x). У него есть дополнительное преимущество работы с любым UIView, что и привело меня сюда.

Jablair 12.02.2010 07:09

Как упоминалось ниже, не забудьте включить #import <QuartzCore / QuartzCore.h>

Gordon Christie 16.05.2010 14:51

Ура! Это здорово, когда у вас есть UIImageView ... но вопрос касается UIImage, не так ли? (Если скорость является проблемой, и вы не хотите выделять объекты UIImageView, то есть.)

Joe D'Andrea 04.05.2012 06:34

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

Michael 28.04.2013 19:05

Вопрос был «Закругленные углы на UIImage» (не uiimageview), поэтому желательно, чтобы ответ был тоже.

Jonny 03.09.2013 10:51

ха! Забавно, что я даже не добавил QuartzCore.framework в свой проект, и это работает как шарм в iOS 7. Есть ли у меня фреймворк где-то еще, а не Link Binary With Libraries !?

Maziyar 23.05.2014 06:33

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

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

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

UIImage + adds.h

#import <UIKit/UIKit.h>

@interface UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS;
@end

UIImage + дополнения.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

Меня сбивает с толку то, что этот вопрос до сих пор получает так много ответов. Меня также сбивает с толку мысль, что у нас не было таких вещей, как UIBezierPath, когда я задал вопрос (iOS 2.0). Человек, мы прошли долгий путь.

Jablair 03.09.2013 23:21

@Jablair - Что ж, поскольку Apple решила, что изображения контактов должны быть в круговом виде в iOS 7, теперь нам нужно много круговых UIImages.

ArtOfWarfare 17.08.2014 18:45

Это должно быть отмечено как правильный ответ, все остальные ответы неверны, если UIImageView не использует режим содержимого UIViewContentModeScaleToFill. Это правильно работает с любым режимом содержимого.

SPQR3 18.07.2015 16:13

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

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

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

Другие вопросы по теме