I am trying to use SwiftUI to write Exif data. When writing in the exposure bias image property
kCGImagePropertyExifExposureBiasValue
It seems to work just fine for all values I've tried except for 2/3.
var exifDictionary: [CFString: Any] = [:]
let expBias: Double = 2/3
exifDictionary[kCGImagePropertyExifExposureBiasValue] = expBias
When inspecting the image in Apples Preview, Photos and using ExifTool each application shows the value as either 0.5 or 1/2 not 0.66 or 2/3 as you'd expect. Similar values such as
let expBias: Double = 5/3
create no problems and are shown as expected either as 1.7, 1.667 or 5/3.
Has anyone else had any experience with this?
In the debug area after writing the data I get one of these messages per image, it doesn't appear to change with the value of the exposure bias.
CleanupRecodeProperties:3604: IIOReadPlugin::CleanupRecodeProperties: top: 1 tiff: 1
CleanupRecodeProperties:3604: IIOReadPlugin::CleanupRecodeProperties: top: 6 tiff: 6
That is the gist of what's going on, the exif seems to be writing correctly however below I will post my code for writing the data to the image file.
import SwiftUI
struct WriteDataView: View {
@State var chosenFileName = "Filename"
@State var realURL: URL?
@State var directoryURLS: [URL] = []
@State var finished = false
func fetchFileURLs() {
do {
let directoryURLS = try FileManager.default.contentsOfDirectory(
at: realURL!,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles
)
let extensions = ["tif", "tiff", "jpg", "jpeg", "png", "JPG"]
var wantedURLs: [URL] = []
for i in (directoryURLS) {
if extensions.contains(i.pathExtension) {
wantedURLs.append(i)
}
}
let sortedDirectoryURLS = wantedURLs.sorted { $0.lastPathComponent < $1.lastPathComponent }
self.directoryURLS = sortedDirectoryURLS
} catch {
print("Error fetching file URLs: \(error.localizedDescription)")
}
}
public func writeExifFunction(_ directoryURLS: [URL]) {
for (index, _) in directoryURLS.enumerated() {
var exifDictionary: [CFString: Any] = [:]
let expBias: Double = 2/3
exifDictionary[kCGImagePropertyExifExposureBiasValue] = expBias
var exifData: [CFString: Any] = [:]
exifData[kCGImagePropertyExifDictionary] = exifDictionary
let imageURL = directoryURLS[index]
addEXIFDataToImage(imageURL: imageURL, exifData: exifData)
}
}
public func addEXIFDataToImage(imageURL: URL, exifData: [CFString: Any]) {
guard let imageData = try? Data(contentsOf: imageURL),
let source = CGImageSourceCreateWithData(imageData as CFData, nil),
let imageType = CGImageSourceGetType(source) else {
print("Failed to load image data or determine image type.")
return
}
let destinationData = NSMutableData()
guard let destination = CGImageDestinationCreateWithData(destinationData, imageType, 1, nil) else {
print("Failed to create image destination.")
return
}
CGImageDestinationAddImageFromSource(destination, source, 0, exifData as CFDictionary)
CGImageDestinationFinalize(destination)
try? destinationData.write(to: imageURL, options: .atomic)
finished = true
}
var body: some View {
if !finished {
VStack{
if chosenFileName == "Filename" {
Spacer()
Button(action: {
let panel = NSOpenPanel()
panel.title = "Find Images to add Exif"
panel.allowsMultipleSelection = false
panel.canChooseDirectories = true
panel.canChooseFiles = false
if case .OK = panel.runModal() {
if !panel.urls.isEmpty {
realURL = panel.urls.first
fetchFileURLs()
chosenFileName = "FilesGot"
}
else {
print("No files selected")
}
}
}) {
VStack{
HStack{
Image(systemName: "film")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.foregroundColor(Color.gray)
Text("FolderSelect")
.foregroundColor(Color.gray)
}
.padding()
.background(RoundedRectangle(cornerRadius: 8))
}
}
.buttonStyle(PlainButtonStyle())
.help("FileBrowserHelp")
Spacer()
.toolbar {
ToolbarItemGroup {
Button {
} label: {
EmptyView()
}
}
}
}
else if chosenFileName != "Filename" {
Text(directoryURLS.count.description)
.toolbar {
ToolbarItemGroup {
Button(action: {
writeExifFunction(directoryURLS)
}) {
Image(systemName: "play")
}
.keyboardShortcut(KeyEquivalent.return, modifiers: [.command])
.help("PlayButtonHelp")
}
}
}
}
}
else {
VStack{
Text("Done")
}
}
}
}
Writing the Double as 0.6 defaults back to 0.5 a value larger than 0.6000003 displays as 0.6 so why doesn't 2/3 write as expected?
When googling the line printed in the debug area nothing comes up.
I simply don't understand why a value like 5/3 or even -2/3 works but 2/3 doesn't.