dkcas11 dkcas11 - 5 months ago 41
Swift Question

UIImage in UIButton pixelated with Vector Image

i am trying to put in an image in a UIButton in XCode with Swift. The button is in a keyboard extension and i am trying to put in "Globe" icon like the one iOS already has in the built-in keyboard. I used to use the globe emoji and the unicode (which automatically get picked up as an emoji), but that didn't look good.

So now, my image is all blurry, no matter which file i use. So far, i've used images from 22pt, 200pt, 600pt, svg and pdf. With no luck.

This is what it looks like:
Blurry image

This is how the Apple one looks like, all sharp: Sharp image

My code for the button is this:

//Set an image to fit inside a button
let btn = nextKeyboardButton
let image = UIImage(named: "globe")!
var scaledImage = image
var targetWidth : CGFloat = 0
var targetHeight : CGFloat = 0
let spacing : CGFloat = 10

if(btn.frame.width < btn.frame.height){
targetWidth = round(btn.frame.width - (spacing * 2))
targetHeight = targetWidth
} else {
targetHeight = round(btn.frame.height - (spacing * 2))
targetWidth = targetHeight

UIGraphicsBeginImageContext(CGSizeMake(targetWidth, targetHeight))
image.drawInRect(CGRectMake(0, 0, targetWidth, targetHeight))
scaledImage = UIGraphicsGetImageFromCurrentImageContext()

btn.setImage(scaledImage, forState: .Normal)
btn.setTitle("", forState: .Normal)
btn.tintColor = UIColor.blackColor()

As the icon is 1:1, i use the same width and height. But i will need the same (working) approach for another icon/image too. Any other icons/images i have used in 25, 50 and 75pts for my tab bar looks good and sharp. It also doesn't matter which simulator i use, if its iPhone 4s or 6 Plus.

Also, might there be any other solutions to this? I have tried to use another font which is all globes, but XCode wouldn't list it in the Editor. I have also tried to paint over the globe emoji, use the unicode character, but that gets picked up as an emoji.... So.. Any help? :)


Your main issue is that UIGraphicsBeginImageContext does not perform device specific scaling. On @2x screen, each point is represented by a single pixel rather than a 2x2 pixel square; it looks pixilated because it is.

Switching to UIGraphicsBeginImageContextWithOptions(size, NO, 0) and providing a sufficiently large should cause it to render crisply.

Another issue to be aware of is that PDF assets get rendered to multiple resolutions of PNG files at build time rather than in the app, so if you rendering a 22x22 image to 20x20 (for example) will cause some level of blurring that you wouldn't expect had the vector image been rendered on device.