Creating a CFDictionary - swift

In an attempt to get this following code to work:
import ImageIO
if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) {
let options: CFDictionary = [
kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
kCGImageSourceCreateThumbnailFromImageIfAbsent: true
]
let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) }
}
I need to know how to correctly initialize a CFDictionary. Unfortunately, it seems like it's not as easy as I predicted.
I've done some experimenting and research, and it seems like there is conflicting information.
For starters, here's an entry in the Apple Docs regarding the kCGImageSourceThumbnailMaxPixelSize key:
kCGImageSourceThumbnailMaxPixelSize
The maximum width and height in pixels of a thumbnail. If this key is not specified, the width and height of a thumbnail is not limited and thumbnails may be as big as the image itself. If present, this key must be a CFNumber value. This key can be provided in the options dictionary that you pass to the function CGImageSourceCreateThumbnailAtIndex.
After looking into how to initialize the CFNumber, I found an excerpt for CFNumber
CFNumber is “toll-free bridged” with its Cocoa Foundation counterpart, NSNumber. This means that the Core Foundation type is interchangeable in function or method calls with the bridged Foundation object
I then tried to do this:
let options: CFDictionary = [
kCGImageSourceThumbnailMaxPixelSize: NSNumber(double: 3.0)
]
and was greeted by the error message: '_' is not convertible to 'CFString!' and Type of expression is ambiguous without more context.

Here is your working code:
func processImage(jpgImagePath: String, thumbSize: CGSize) {
if let path = NSBundle.mainBundle().pathForResource(jpgImagePath, ofType: "") {
if let imageURL = NSURL(fileURLWithPath: path) {
if let imageSource = CGImageSourceCreateWithURL(imageURL, nil) {
let maxSize = max(thumbSize.width, thumbSize.height) / 2.0
let options : [NSString : AnyObject] = [
kCGImageSourceThumbnailMaxPixelSize: maxSize,
kCGImageSourceCreateThumbnailFromImageIfAbsent: true
]
let scaledImage = UIImage(CGImage: CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options))
// do other stuff
}
}
}
}
From Docs:
The implicit conversions from bridged Objective-C classes
(NSString/NSArray/NSDictionary) to their corresponding Swift value
types (String/Array/Dictionary) have been removed, making the Swift
type system simpler and more predictable.
The problem in your case are the CFStrings like kCGImageSourceThumbnailMaxPixelSize. These are not automatically converted to String anymore.
reference from HERE.

Related

Retrieve NSDictionary Value with swift 4

I try to retrieve exif data from a picture.
I can load it in a Dictionary, but I am unable to use this Dictionary.
my Current code is :
import Cocoa
import ImageIO
let path = "/Volumes/Olivier/Original/Paysage/affoux/_OPI7684.NEF"
let UrlPath = URL(fileURLWithPath: path)
UrlPath.isFileURL
UrlPath.pathExtension
UrlPath.hasDirectoryPath
let imageSource = CGImageSourceCreateWithURL(UrlPath as CFURL, nil)
let imageProp = CGImageSourceCopyPropertiesAtIndex(imageSource!, 0, nil)
var key = "kCGImagePropertyWidth" as NSString
let h :NSDictionary = CFDictionaryGetValue(imageProp, kCGImagePropertyWidth)
The last line, doesn't work at all.
Any solution ?
Thank's
The problem is that your key name is wrong. You mean kCGImagePropertyPixelWidth. And it's not a string. It's a constant. So it should not be in quotes; just use the constant directly, and don't worry what its value is.
I would suggest also that you convert to a Swift dictionary earlier in the process. Here is actual working code that you can model yourself after:
let src = CGImageSourceCreateWithURL(url as CFURL, nil)!
let result = CGImageSourceCopyPropertiesAtIndex(src, 0, nil)!
let d = result as! [AnyHashable:Any]
let width = d[kCGImagePropertyPixelWidth] as! CGFloat
let height = d[kCGImagePropertyPixelHeight] as! CGFloat
Of course that code is pretty bad because every single line contains an exclamation mark (which means "crash me"), but in real life I don't crash, so I've allowed it to stand.

NSData from CGImageRef in Swift

I am having difficulty figuring out how to get an NSData representation of an image from a CGImageRef. All of the answers I've found make use of UIImage or NSImage, but my application is cross-platform, so I want to use only Core Graphics. Objective-C answers state simply that CFData is toll-free bridged to NSData and simply cast it, but Swift will not allow this. The closest I've got is:
var image: CGImageRef? = nil
//...
if let dataProvider: CGDataProviderRef = CGDataProviderCreateWithURL(url) {
image = CGImageCreateWithPNGDataProvider(dataProvider, nil, false, CGColorRenderingIntent.RenderingIntentDefault)
// works fine
//...
if let data = CGDataProviderCopyData(CGImageGetDataProvider(image)) as? NSData {
// Do something with data, if only it ever got here!
}
}
but the cast doesn't ever succeed...
CGDataProviderCopyData() returns the optional CFData?, and that cannot be cast to the non-optional NSData. But you can convert/bridge
it to NSData? and use that in the optional binding:
if let data = CGDataProviderCopyData(CGImageGetDataProvider(image)) as NSData? {
// Do something with data ...
}
Here is a simpler example demonstrating the same issue with
CFString and NSString:
let cfstr : CFString? = "Hello world"
if let nsstr = cfstr as? NSString {
print("foo") // not printed
}
if let nsstr = cfstr as NSString? {
print("bar") // printed
}
But I admit that my explanation is not fully satisfying, because
a similar optional cast works in other cases:
class MyClass { }
class MySubclass : MyClass { }
let mc : MyClass? = MySubclass()
if let msc = mc as? MySubclass {
print("yes") // printed
}
So this must be related to the toll-free bridging between CoreFoundation
and Foundation types.

CFDictionary won't bridge to NSDictionary (Swift 2.0 / iOS9)

OK, this is a case I came across when working with CGImageSource and noticed that the toll-free-bridging between CFDictionary and NSDictionary seems to run into problems in certain cases. I've managed to construct the below example to show what I mean:
func optionalProblemDictionary() -> CFDictionary? {
let key = "key"
let value = "value"
var keyCallBacks = CFDictionaryKeyCallBacks()
var valueCallBacks = CFDictionaryValueCallBacks()
let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, UnsafeMutablePointer(unsafeAddressOf(key)), UnsafeMutablePointer(unsafeAddressOf(value)), 1, &keyCallBacks, &valueCallBacks)
return cfDictionary
}
Fairly straightforward (and a bit silly) but its a function returning and optional CFDictionary. The "fun" starts when trying to create an NSDictionary from this function:
Why won't the following work?
if let problemDictionary = optionalProblemDictionary() as? NSDictionary {
print(problemDictionary) // never enters, no warnings, compiles just fine
}
While this works fine?
if let cfDictionary = optionalProblemDictionary() {
let problemDictionary = cfDictionary as NSDictionary
print(problemDictionary)
}
XCode 7.0 (7A220)
The reason seems to be that the function returns an optional
CFDictionary? and that can not be cast to a (non-optional)
NSDictionary.
Here is a simpler example demonstrating the same problem with CFString vs NSString:
let cfString = "foobar" as CFString?
if let s1 = cfString as? NSString {
print("s1 = \(s1)") // not executed
}
(The question remains why this does not give a compiler error or
at least a compiler warning because this optional cast can
never succeed.)
But a casting to an optional NSString? works:
if let s2 = cfString as NSString? {
print("s2 = \(s2)") // prints "s2 = foobar"
}
In your case, if you change the "problematic case" to
if let problemDictionary = cfDict as NSDictionary? {
print(problemDictionary)
}
then the if-block is executed.
Note that your method to build a CFDictionary in Swift is not correct
and actually caused program crashes in my test. One reason is that
the dictionary callbacks are set to empty structures.
Another problem is that unsafeAddressOf(key) bridges the Swift
string to an NSString which can be deallocated immediately.
I don't know what the best method is to build a CFDictionary in Swift,
but this worked in my test:
func optionalProblemDictionary() -> CFDictionary? {
let key = "key" as NSString
let value = "value" as NSString
var keys = [ unsafeAddressOf(key) ]
var values = [ unsafeAddressOf(value) ]
var keyCallBacks = kCFTypeDictionaryKeyCallBacks
var valueCallBacks = kCFTypeDictionaryValueCallBacks
let cfDictionary = CFDictionaryCreate(kCFAllocatorDefault, &keys, &values, 1, &keyCallBacks, &valueCallBacks)
return cfDictionary
}

CMFormatDescription to CMVideoFormatDescription

I'm trying to get the resolution of the camera of a device using swift.
I'm using CMVideoFormatDescriptionGetDimensions which requires a CMVideoFormatDescription, but AVCaptureDevice.formatDescription returns a CMFormatDescription. I've tried a multitude of ways to cast CMFormatDescription to CMVideoFormatDescription and can't seem to get it working.
Below is a sample of the code that I'm using:
for format in device.formats as [AVCaptureDeviceFormat] {
let videoDimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
}
This doesn't seem possible in Swift at the moment. One solution then would be to write a helper function in objective-c, such as:
CMVideoDimensions CMFormatDescriptionGetDimensions(CMFormatDescriptionRef formatDescription)
{
if (CMFormatDescriptionGetMediaType(formatDescription) == kCMMediaType_Video)
return CMVideoFormatDescriptionGetDimensions(formatDescription);
else
return (CMVideoDimensions) {
.width = 0,
.height = 0
};
}
Include the header with the function prototype in the Swift bridging header so that it will be accessible as a global function from your Swift code.
I was able to get the resolution using the swift method below:
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) as AVCaptureDevice
let formatDesc = captureDevice.activeFormat.formatDescription
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
Here's a solution in pure Swift, really only usable for logging purposes and such. Paste the following function in your class or somewhere else:
func widthAndHeightFromTrack(track: AVAssetTrack) -> CGSize {
let str = track.formatDescriptions.description
let regex = try! NSRegularExpression(pattern: "[0-9]{2,4} x [0-9]{2,4}", options: [])
if let result = regex.firstMatchInString(str, options: [], range: NSMakeRange(0, str.characters.count)) {
let dimensionString = (str as NSString).substringWithRange(result.range)
let dimensionArray = dimensionString.componentsSeparatedByString(" x ")
let width = Int(dimensionArray[0])
let height = Int(dimensionArray[1])
return CGSize(width: width!, height: height!)
}
return CGSizeZero
}
Example usage:
let allTracks: AVAsset = someAVAsset.tracksWithMediaType(AVMediaTypeVideo)
let videoTrack = allTracks[0]
let videoTrackDimensions = widthAndHeightFromTrack(videoTrack)
// You now have a CGSize, print it
print("Dimensions: \(videoTrackDimensions)")
Of course, the above solution will completely break whenever Apple changes something in the string representation of the CMFormatDescription. But it's useful for logging the dimensions.
Maybe, question is too old, but the Swift issue is still not fixed.
public extension AVURLAsset {
var audioFormatDescription: CMAudioFormatDescription? {
if let track = self.tracks(withMediaType: .audio).first,
let untypedDescription = track.formatDescriptions.first {
// hacks, warnings, disablings of swiftlint below are wrork-around of
// Swift bug: it fails converting 'Any as CMFormatDescription'
let forceTyped: CMFormatDescription?
//swiftlint:disable force_cast
= untypedDescription as! CMAudioFormatDescription
//swiftlint:enable force_cast
if let description = forceTyped {
return description
} else {
return nil
}
} else {
return nil
}
}
}

How to get Image Name used in an UIImage?

I am using an UIImage, in which I have an Image, and I want to know the name of image.
That functionality is not built-in to UIImage because images are not always loaded from files. However, you could create a custom UIImageView subclass to fit your needs.
It is not possible. UIImage instance contains the actual image data without any reference to the filename.
Images do not necessarily come from files or other named sources, so not all images even have a name. When you create an image from a file, you could store the name in a separate NSString*, and then refer to that stored name when necessary.
this code will help you out
NSString *imgName = [self.imgView1st image].accessibilityIdentifier;
NSLog(#"%#",imgName);
[self.imgView2nd setImage:[UIImage imageNamed:imgName]];
On later iOS versions it's possible to extract image name from the description. Aware, for the debug use only!
extension UIImage {
/// Extracts image name from a description.
/// * Example description: `<UIImage:0x60000278ce10 named(main: ic_timeline_milestone_bluedot) {16, 16}>`
/// * Example name: `ic_timeline_milestone_bluedot`
/// - warning: For the debug use only.
var name: String? {
let description = self.description
guard let regexp = try? NSRegularExpression(pattern: "\\(main: (.*)\\)", options: []) else { return nil }
guard let match = regexp.matches(in: description, options: [], range: description.fullRange).first else { return nil }
guard match.numberOfRanges > 0 else { return nil }
let idx1 = description.index(description.startIndex, offsetBy: range.lowerBound)
let idx2 = description.index(description.startIndex, offsetBy: range.upperBound)
return String(description[idx1..<idx2])
}
}
This answer (https://stackoverflow.com/a/72542728/897465) has (what I believe) the best answer:
let img = UIImage(named: "something")
img?.imageAsset?.value(forKey: "assetName")
Here's a handy extension for it:
extension UIImage {
var containingBundle: Bundle? {
imageAsset?.value(forKey: "containingBundle") as? Bundle
}
var assetName: String? {
imageAsset?.value(forKey: "assetName") as? String
}
}