Get a CGImageRef from an IKImageView in Swift

2.4k Views Asked by At

I'm trying to get the image associated with a view but Xcode returns me an error. Here is the code:

@IBOutlet var imageView: IKImageView // Link to the image view.

func saveImage() {
    var newImage: CGImageRef = imageView.image() // Line with an error.
}

I have a awakeFromNibfunction to set the image in the view. The code above returns me 'Unmanaged<CGImage>' is not convertible to 'CGImageRef'.

I know that types are optional in Swift but I don't understand why I get this error, the IKImageView Class Reference clearly said that get IKImageView's image return a CGImageRef.

2

There are 2 best solutions below

5
On BEST ANSWER

This has to do with the bridging of Objective C APIs into Swift. The Swift versions of an API have been bridged (mostly automatically, I believe) by converting the original Objective C headers into a Swift version. In many cases, the conversion can work out what kind of value you're going to get back, and therefore in Swift you get a "normal" type that you can use as you'd expect.

However, the conversion is still a work in progress. In this case, what you're seeing is a type used to help you manage memory manually when the conversion process doesn't know whether you're going to get back an object that's been retained for you or not, because the API doesn't have the conventional annotation that it could use to figure it out.

The Unmanaged type effectively says "you have to tell me what to do with the contained value to get the memory management right". As soon as you get an Unmanaged value, you should call either one of its two methods:

takeUnretainedValue() 
takeRetainedValue()

...depending on whether the object you get back was a "+0" or "+1". So, you're expected to do something like:

var newImage: CGImage = imageView.image().takeUnretainedValue()

And as soon as you've done that (which you should do pretty much immediately), you've given Swift enough of a hint that it can now appropriately manage the object correctly with ARC, and got yourself a valid reference of the right type.

This is touched on briefly at the end of the "Swift Interoperability in Depth" WWDC video from this year.

0
On

Look at the image() method in the new version of the IKImageView reference that shows Swift declarations and you'll see it returns an Unmanaged<CGImage>!. That means that ImageKit isn't set up such that the compiler can automatically infer CoreFoundation memory management semantics on its API. (This affects both Swift translation and ObjC implicit ARC bridging.) It should, so that'd be a good bug to file.

The Swift type Unmanaged<T> is how you interact with CF types when the compiler can't automatically infer their ARC behavior. To get at the underlying type, call either takeUnretainedValue() or takeRetainedValue() — you have to choose which depending on whether the API you're calling is known to already be retaining the value it returns. Exciting guesswork! (Again, eliminating guesswork would be a good bug to file.) You can read more about this in Using Swift with Cocoa and Objective-C.

Anyway, in this case you can probably expect that IKImageView is not incrementing the retain count of the CGImage when providing it, so you can unwrap the Unmanaged like so:

var newImage: CGImage = imageView.image().takeUnretainedValue()

Also note you don't need the Ref suffix when working with CF types in Swift. (CGImageRef is a typealias to CGImage.) Swift lets you work with CF types as if they're Swift objects (instead of as C pointers to opaque C types).