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





Я не могу рассказать о вашей аварии, но я подумал, что предлагаю другой вариант для скругления углов. У меня возникла аналогичная проблема в приложении, над которым я работал. Вместо того, чтобы писать какой-либо код, я накладываю другое изображение, которое маскирует углы.
Я пробовал замаскировать изображение и так и не смог заставить его работать надежно / вообще. У вас есть какие-нибудь указания на то, как вы заставили это работать? Это было недавно, поэтому я точно не помню, что пробовал, но думаю, что прошел через CGImageCreateWithMask, CGImageMaskCreate и CGContextClipToMask.
Я не маскируюсь в коде. Я просто рисую еще один UIImage поверх изображения, которое хочу замаскировать. UIImage имеет прозрачную секцию посередине и черные (мой цвет фона) края, которые я хочу «замаскировать».
Если он дает сбой только в некоторых случаях, выясните, что общего у этих случаев сбоя. Каждый раз dstRect одно и то же? Изображения когда-нибудь бывают разного размера?
Кроме того, вам нужен CGPathRelease(borderPath), хотя я сомневаюсь, что утечка является причиной вашей проблемы.
Спасибо за напоминание о выпуске. Я случайно закомментировал это с помощью другого отладочного кода, который затем обрезал перед публикацией. dstRect то же самое, но изображения могут быть разными, потому что они загружены из Интернета. Получаю от тестера - сам не перепроверял.
Самый простой способ - встроить отключенную [!] Кнопку с круглым прямоугольником [не настраиваемую!] В ваше представление (можно даже сделать все это в Интерфейсном Разработчике) и связать с ней свое изображение. Сообщение о настройке изображения отличается для UIButton (по сравнению с UIImageView), но общий кладж работает как шарм. Используйте setImage: forState: если вам нужен значок по центру или setBackgroundImage: forState: если вы хотите, чтобы все изображение было вырезано по углам (например, для контактов). Конечно, если вы хотите отображать много этих изображений в drawRect, это неправильный подход, но, скорее всего, встроенное представление - это именно то, что вам нужно в любом случае ...
Если вы вызываете свой метод (borderedImageWithRect) в фоновом потоке, могут произойти сбои, поскольку функции UIGraphics не являются потокобезопасными. В таком случае вы должны создать контекст с помощью CGBitmapContextCreate () - см. Пример кода «Отражение» из SDK.
Фьоахим, ты на самом деле это сделал? У меня проблема с рисованием в контексте растрового изображения в фоновом потоке. См. stackoverflow.com/questions/702914/…
Я бы еще раз повторил ответ фьоахима: будьте осторожны при попытке рисования во время работы в отдельном потоке, иначе вы можете получить ошибки 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, что и привело меня сюда.
Как упоминалось ниже, не забудьте включить #import <QuartzCore / QuartzCore.h>
Ура! Это здорово, когда у вас есть UIImageView ... но вопрос касается UIImage, не так ли? (Если скорость является проблемой, и вы не хотите выделять объекты UIImageView, то есть.)
Просто замечание для всех, кто сталкивается с той же проблемой, что и я - вы должны установить изображение для просмотра изображения ДО того, как закруглить углы.
Вопрос был «Закругленные углы на UIImage» (не uiimageview), поэтому желательно, чтобы ответ был тоже.
ха! Забавно, что я даже не добавил QuartzCore.framework в свой проект, и это работает как шарм в iOS 7. Есть ли у меня фреймворк где-то еще, а не Link Binary With Libraries !?
Если 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 - Что ж, поскольку Apple решила, что изображения контактов должны быть в круговом виде в iOS 7, теперь нам нужно много круговых UIImages.
Это должно быть отмечено как правильный ответ, все остальные ответы неверны, если UIImageView не использует режим содержимого UIViewContentModeScaleToFill. Это правильно работает с любым режимом содержимого.
Установите изображение в формате 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
}
Почему вы используете CGContextSaveGState () и CGContextRestoreGState ()? Читая документацию, у меня создается впечатление, что состояние является внутренним по отношению к контексту, и весь контекст в любом случае отбрасывается.