Month: April 2015

Swift:EXIFアプリのUI作成に着手

指示されたファイル名からEXIFの情報を表示し、修正するアプリのベース部分を作りはじめました。

早速、おかしなことに、NSImageViewに読み込んだjpgファイルを表示できません。AppKitが足りないことはすぐにわかったのですが、既存のプログラムの中ではうまくいくのに、仮称アプリMesaEXIFでは全然ダメです。

結局、NSTextFieldに入れたファイルパスとファイルタイプが正しいかどうか調べてからNSImageViewに入れるようにしたら、写真が表示できました。やれやれです。

少し迷ったところは、xbImage?.image = NSImage(data: wimageData)!の.imageとNSImage(  )。これはMesaClipでも迷ったところでした。

@IBOutlet var   xbImage:NSImageView?

func ExecImageFromFilepath(pFilepath:String) {
var wimageURL: NSURL = NSURL(fileURLWithPath: pFilepath)!  //NSURLを作成
var wimageData: NSData  = NSData(contentsOfURL: wimageURL)! //写真を読み込む。
xbImage?.image = NSImage(data: wimageData)! //読み込んだ写真を表示。
}

便利にMac:自動起動アプリの停止の仕方

ここ数日、再起動したログイン直後の調子が悪いので、自動起動アプリを外してみました。

Mac OS X 10.10.4(beta?)に先程アップデートして、それでも再起動直後の様子がおかしいので、自動起動するアプリのうち次のものを外しました。

  • キヤノンデジカメ関係のアプリ
  • Flickrのアップローダー

自動起動させない方法をすぐに思いつかなかったので、メモしておきます。

システム環境設定➡️ユーザーとグループ

チェックが入っているものを自動起動しないようにしたつもりだったのですが、チェックを入れただけではダメで、しっかり一覧から削除しないといけませんでした。

スクリーンショット 2015-04-28 20.28.31結果、OKになりました。再ログイン直後にWi-Fiがぐずぐずしていたり、ウイルス対策ソフトやんVPNの立ち上がりが悪いようでしたら、自動起動しているアプリケーションを見直した方がいいです。頻繁に使わなかったり、手動でも構わないものなら、わざわざ便利さを取って障害を引き起こすのは考えものです。

Swift:EXIFの書き込み

EXIFの書き込みは、当初シナリオ通り、写真ファイルと一緒に書き込むことで実現しました。

これは、正直、よくわかっていません。

UIImagePickerControllerで撮影した写真にExif・位置情報を書き込んで保存する方法Swiftを使って「ハワイに行ったふりカメラ」をつくるなどを見るには見たのですが、Objective-Cだったり、内容が少し違うような気がして、徹底して追求する根気がありませんでした。

それでも、何となく次のようにやったらEXIFの書き込みができてしまいました。もしかすると、まちがいがあるかもしれませんが、今後、UIを作っていく段階でバグがあれば修正する機会があると思います。

func execWriteEXIF(pFilepath:String, pEXIF:NSDictionary, pExportFilepath:String) { //
var wimageURL: NSURL = NSURL(fileURLWithPath: pFilepath)! //写真のFilepathからNSURLを作成する。
var cgImage:CGImageSourceRef = CGImageSourceCreateWithURL (wimageURL, nil)//イメージソース作成
var imageData:NSMutableData = NSMutableData()//
var dest:CGImageDestinationRef  = CGImageDestinationCreateWithData (imageData, CGImageSourceGetType(cgImage), 1, nil)//
CGImageDestinationAddImageFromSource (dest, cgImage, 0, pEXIF as Dictionary )//
CGImageDestinationFinalize(dest) //
imageData.writeToFile(pExportFilepath, atomically: true) //写真ファイルの書き込み
}

細かい話として、緯度・経度のシンタクス・チェックをきっちりしないと、プレビューが落ちます。90度、180度、60分、60秒の最大値チェックとEWNSチェックは必須。また、-はマイナスでなく区切りのようです。これは、除去しないといけません。

Swift:EXIFを書き換え

第一段階として、GPS辞書の変換・削除・差し替え、EXIFを書き換えができるようになりました。

ちょっとしたことなのですが、時間がかかってしまいました。いろいろ調べたのですが、残念なことに決め手になるものを見つけられませんでした。

NSDictionaryとDictionaryの相互変換

いくつか重要なことがあると思われますので、メモします。

  • NSDictionaryはwDicGPS[kCGImagePropertyGPSLatitude] = wGPS.pLatiのような書き方で簡単に値の変更ができません。一方、EXIF辞書はNSDictionaryでないと読み込み・書き込みができないようです。それでNSDictionaryにEXIFを読み込み → Dictionary & 値の設定・変更 → NSDictionary → 書き込みという手順を踏まえたら、EXIFの書き換えができました。

var mm_EXIF:NSDictionary  = NSDictionary()
var mm_GPS:NSDictionary = NSDictionary()

  • それで、一旦、Dictionaryに変換してから値をセットし、そのあとでNSDictionaryに戻せばうまくことがわかりました。
  • NSDictionaryへの読み込み

mm_EXIF = CGImageSourceCopyPropertiesAtIndex (wimageRef, 0, nil)
mm_GPS  = mm_EXIF.objectForKey (kCGImagePropertyGPSDictionary) as! NSDictionary

  • Dictionaryへの変換 ワーク用のDictionaryをグローバル変数で定義するとエラーが発生します。

var wDicEXIF:Dictionary = mm_EXIF   as Dictionary
var wDicGPS:Dictionary  = mm_GPS

  • Dictionaryに値を設定

wDicGPS[kCGImagePropertyGPSLatitude]  = wGPS.pLati//緯度

  • 書き換えするためにNSDictionaryに再変換

wDicEXIF.removeValueForKey (kCGImagePropertyGPSDictionary)//GPS削除
wDicEXIF[kCGImagePropertyGPSDictionary] = wDicGPS//GPS追加
mm_EXIF = wDicEXIF//NSDictionaryに再変換

このあと、jpgファイルといっしょにmm_EXIFを書き込めばEXIFの更新ができます。

参考:NSArrayやNSDictionaryを、SwiftのArrayやDictionaryと相互変換

Swift:EXIFの書き込みアプローチ

EXIFの書き込み方がわからないので、どのようにアプローチしていけばいいか、頭の中を整理することにしました。

いずれにしてもひとりごとです。なお、GPSプロパティを読み込んで緯度・経度に変換する、Google Mapsの緯度・経度をGPSプロパティに変換するメソッドは、テストプログラムから分離し、GPSアプリとして独立させました。

EXIFの読み込み方は?

  1. 写真のファイルパスをNSURLに変換。
  2. イメージソースの作成。
  3. EXIFを含む辞書全体(プロパティ)が読み込める。
  4. プロパティからGPSだけ部分的に読み込める。

var filepath:String = NSHomeDirectory() + “/Desktop/test.jpg”
var wimageURL: NSURL = NSURL(fileURLWithPath:filepath)!//NSURL
var wimageRef:CGImageSourceRef = CGImageSourceCreateWithURL (wimageURL as CFURLRef, nil) //イメージソース
var wimageDic:NSDictionary = CGImageSourceCopyPropertiesAtIndex (wimageRef, 0, nil) //EXIFやGPSを含むプロパティ全体
var wgps:AnyObject? = wimageDic.objectForKey (kCGImagePropertyGPSDictionary) //GPSのみ抽出

※ CGImageSourceの中には、写真のプロパティを参照するメソッドは存在するが、書き込むものは見あたらない。

CGImageはキーワードでは?

NSBitmapImageRepが大元にあり、JPEGやPINGがその中に含まれているらしい。ここから探し出すとなると、量が多いので厄介そう。

  • プロパティは、写真画像をsaveするときに一緒に書き込まれるというようなobjective-Cのサンプルコードがあったような気がする。今のところ、これが唯一の手がかり。
  • もし写真を読み込む必要があるなら、① 画像劣化しない方法が何か、② Photos(写真)を直接ハンドリングする方法は何か、この2点の解明も必要。
  • 現在想定している緯度・経度修正は、Google Mapsの緯度・経度をcopy & pasteすることを前提にしている。
  • しかし、地図を表示し、ピンから緯度・経度を拾うような方法の検討も必要か?
  • 地図を入れた機能を実現するには、ディベロッパー契約が必要。
  • それでは、Google MapsのAPIを使えないか?
  • Google MapsのAPIはiOSはあるようだが、OS Xは見あたらなかった。ないのか?
  • サムネール表示の検討。

こうなってくると、「写真の書き込み & EXIFの作成・書き込み」から当ってみてはどうだろう。あるいは「写真のsave」のどこかにプロパティをどうするかというのがある可能性は?

Swift:1文字抽出

Stringを1文字ずつ抽出する方法は?

for inを使わなくても1文字ずつ抽出できることを学習しました。本当にやれやれです。

5文字目を1文字抽出する方法

let str = “HelloWorld!”
 var w1:String = String(str[advance(str.startIndex, 5)])  // “W”

ニューメリックチェックは単純に”0″ < String < “9”

var pNum:String = “2”
if pNum >= “0” && pNum <= “9” {

開発状況

ここまでで次の作業が終わりました。

  • EXIF(実際にはGPS辞書)の緯度・経度を度・分・秒に変換して表示する。
  • Google Mapsの位置(緯度・経度)を度・分・秒からGPS辞書の形式に変換する。GPS辞書への書き込みは未了。
  • 変換ロジックのテストは終了。

Swift:緯度・経度のシンタクスチェック

緯度・経度はどうなっているの?

緯度・経度を表示するロジックはできあがりました。

次に写真プロパティに緯度・経度をGoogle Mapsから入れるロジックを作ろうとしたら、そのcopy & pasteは問題ないとしても、手入力した場合、マチガイをどうチェックすればいいか「ハテナ?」となりました。

不勉強というか、常識がないというか、こういうところで素性がばれてしまいます。

緯度

  • 度の値:0〜180
  • 分・秒の値:0〜60
  • 東経:E、西経:W
  • 表記の特例:-41.00’N は41.00’Nと同じでマイナスではないことに注意。

経度

  • 度の値:0〜90
  • 分・秒の値:0〜60
  • 北緯:N、南緯:S
  • 表記の特例:–41.00’S は41.00’Sと同じでマイナスではないことに注意。

Google Mapsの例

  • 33°33’36.7″N 130°12’19.1″E
  • Apple地図は緯度・経度を表示できないのでcopy & pasteには使えない。

Swift:浮動小数点(Float / Double)をString変換

緯度・経度の変換処理のメモです。留意点は、Floatではなく、Doubleを使うこと。

緯度をDoubleにして、一の位以上をIntで取り出す。

var wGPS:Double = wgps!.objectForKey(kCGImagePropertyGPSLatitude) as! Float//緯度をDoubleに変換
var wNo1:Int = Int(wGPS) //DoubleをIntに変換(整数部分抽出)

Doubleを文字列に変換するときの指定と変換結果

let num = 1.23456
var wstring: String
wstring = NSString(format: “%.3f”, num) as String// 1.235
println(“wstring 1=\(wstring)”)
wstring = NSString(format: “%.3F”, num) as String// 1.235
println(“wstring 2=\(wstring)”)
wstring = NSString(format: “%.3e”, num) as String// 1.235e+00
println(“wstring 3=\(wstring)”)
wstring = NSString(format: “%.3E”, num) as String// 1.235E+00
println(“wstring 4=\(wstring)”)
wstring = NSString(format: “%.3g”, num) as String// 1.23
println(“wstring 5=\(wstring)”)
wstring = NSString(format: “%.3G”, num) as String// 1.23
println(“wstring 6=\(wstring)”)

注1:format: “%.3f”場合、小数点以下を3桁表示する。

注2:format: “%.3f”の場合、少数4桁目で四捨五入。

返り値が複数ある場合の呼び出し方とメソッドの書き方

var wStringGPS      = execGPSto60(filepath)
println(“wDate=\(wStringGPS.pLatitude)”)  
println(“wDate=\(wStringGPS.pLongitude)”)   

func execGPSto60(pFilepath:String) -> (pLatitude:String, pLongitude:String) {

return (wLatitude, wLongitude)
}