26
votes

I've got a UIImageView (full frame and rectangular) that i'm rotating with a CGAffineTransform. The UIImage of the UIImageView fills the entire frame. When the image is rotated and drawn the edges appear noticeably jagged. Is there anything I can do to make it look better? It's clearly not being anti-aliased with the background.

8
Is the image being internally anti-aliased -- is it just the edges that are jagged or the whole thing? BTW I would recommend using a CALayer if possible.hatfinch
It's for the iPhone so I believe it's already CALayer backed.Meltemi
Indeed, but why carry all the UIView/UIResponder baggage if you don't need to?hatfinch
well, in my case they need to views need to register touchesMeltemi
and only the edges are jagged... image itself is anti-aliasing just fine.Meltemi

8 Answers

55
votes

The edges of CoreAnimation layers aren't antialiased by default on iOS. However, there is a key that you can set in Info.plist that enables antialiasing of the edges: UIViewEdgeAntialiasing.

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html

If you don't want the performance overhead of enabling this option, a work-around is to add a 1px transparent border around the edge of the image. This means that the 'edges' of the image are no longer on the edge, so don't need special treatment!

28
votes

New API – iOS 6/7

Also works for iOS 6, as noted by @Chris, but wasn't made public until iOS 7.

Since iOS 7, CALayer has a new property allowsEdgeAntialiasing which does exactly what you want in this case, without incurring the overhead of enabling it for all views in your application! This is a property of CALayer, so to enable this for a UIView you use myView.layer.allowsEdgeAntialiasing = YES.

17
votes

just add 1px transparent border to your image

CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[image drawInRect:CGRectMake(1,1,image.size.width-2,image.size.height-2)];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
10
votes

Remember to set the appropriate anti-alias options:

CGContextSetAllowsAntialiasing(theContext, true);
CGContextSetShouldAntialias(theContext, true);
6
votes

just add "Renders with edge antialiasing" with YES in plist and it will work.

5
votes

I would totally recommend the following library.

http://vocaro.com/trevor/blog/2009/10/12/resize-a-uiimage-the-right-way/

It contains lots of useful extensions to UIImage that solve this problem and also include code for generating thumbnails etc.

Enjoy!

2
votes

The best way I've found to have smooth edges and a sharp image is to do this:

CGRect imageRect = CGRectMake(0, 0, self.photo.image.size.width, self.photo.image.size.height);
UIGraphicsBeginImageContextWithOptions(imageRect.size, NO, 0.0);
[self.photo.image drawInRect:CGRectMake(1, 1, self.photo.image.size.width - 2, self.photo.image.size.height - 2)];
self.photo.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Adding the Info.plist key like some people describe has a big hit on performance and if you use that then you're basically applying it to everything instead of just the one place you need it.

Also, don't just use UIGraphicsBeginImageContext(imageRect.size); otherwise the layer will be blurry. You have to use UIGraphicsBeginImageContextWithOptions like I've shown.

0
votes

I found this solution from here, and it's perfect:

+ (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame transparentInsets:(UIEdgeInsets)insets {
    CGSize imageSizeWithBorder = CGSizeMake(frame.size.width + insets.left + insets.right, frame.size.height + insets.top + insets.bottom);
    // Create a new context of the desired size to render the image
    UIGraphicsBeginImageContextWithOptions(imageSizeWithBorder, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // Clip the context to the portion of the view we will draw
    CGContextClipToRect(context, (CGRect){{insets.left, insets.top}, frame.size});
    // Translate it, to the desired position
    CGContextTranslateCTM(context, -frame.origin.x + insets.left, -frame.origin.y + insets.top);

    // Render the view as image
    [view.layer renderInContext:UIGraphicsGetCurrentContext()];

    // Fetch the image
    UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();

    // Cleanup
    UIGraphicsEndImageContext();

    return renderedImage;
}

usage:

UIImage *image = [UIImage renderImageFromView:view withRect:view.bounds transparentInsets:UIEdgeInsetsZero];