JPEGに変換

画像をJPEGに変換するのはとっても簡単。

なんらかの方法でNSBitmapImageRepを作成。

let imgRep = NSBitmapImageRep(...)

あとは、NSBitmapImageFileType.NSJPEGFileType を指定して Data を取得するだけ。
 properties には圧縮率などを辞書として渡せます。

let data = imgRep.representation(using: NSBitmapImageFileType.NSJPEGFileType, properties: [NSImageCompressionFactor:Float(0.8)])

最後に data を適当な場所に書き出せばOKです。

data.write(to: pathString, options:[])

NSImageをリサイズ

意外と複雑な画像のリサイズ。
やり方は色々ありそうですが、今回使った方法を記録しておきます。

大まかな流れは

  1. CGImageを準備
  2. CGContextを準備
  3. リサイズ後のサイズを指定
  4. contextを使って書き込む
  5. NSImageに戻す


まずはあらかじめ準備したNSImageからNSBitmapImageRepを取り出し、さらにCGImageを取り出す。

guard let image = NSBitmapImageRep(data: sourceImage.tiffRepresentation!)?.cgImage else {
    //  エラー処理
}

次に各種パラメータを準備して、CGContextを作成。

let bitsPerComponent = 8
let bytesPerRow = 4 * newWidth
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)

guard let bitmapContext = CGContext(data: nil, width: newWidth, height: newHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
    // エラー処理
}

サイズを指定して書き込む。

let bitmapSize = NSMakeSize(CGFloat(newWidth), CGFloat(newHeight))
let bitmapRect = NSMakeRect(0.0, 0.0, bitmapSize.width, bitmapSize.height)

bitmapContext.draw(image, in: bitmapRect)

最後にNSImageに戻す。

guard let newImageRef = bitmapContext.makeImage() else{
    // エラー処理
}
let newImage = NSImage(cgImage: newImageRef, size: bitmapSize)

NSArrayControllerでNSTableViewをBind!

Touchdownでは複数の設定を保存して切り替えられるようにするため、NSTableViewをUIとして使います。

基本的なとこだけど、何かと分かりにくいCocoa Binding。
iOSでは使えないらしく情報も少なめなので、簡単にまとめておきます。

StoryboardでNSTableViewとNSArrayControllerを配置


NSTableViewとNSArrayControllerをドラッグで配置。


ViewControllerにArrayを定義


例えば文字列の配列なら
dynamic var items: [String] = ["a", "b", "c"]

バインドするためにdynamicを付けるのがポイント


NSTableViewは2箇所Bind


まずNSTableViewとArrayControllerを2箇所バインド。
ContentとはarrangedObjects、Selection IndexesはselectionIndexes。



さらにTableViewCellのValueにTableCellViewのobjectValueをバインド。



値を変更できるようにする


TableViewCellのBehaviorをEditableにしておきます。



コンパイルして実行


これでArrayの中身を表示して編集することができるようになりました。



設計と実装

きっちり設計して後は実装するだけ、というのはある意味理想形の一つかもしれませんが、勉強しながら進めたいので今回は作りながら考えることにします。

とはいえ、気の向くままにコードを書くわけにもいかないので自分ルールは決めておきたいところ。

・Macアプリ設計/実装の流儀に従う
・Cocoa Bindingは積極的に使う
・重い処理はマルチスレッド化する

まずはこのくらいから始めることにします。

アプリの要件洗い出し

何か作ろうと思ったら、まずは要件を整理してみるのが完成への近道。
とはいえ、個人で開発するだけならそれほど厳密になる必要もなく、まずは「最低限これがあればアプリとして成立する」というところを狙います。

譲れないのは以下の点。

  • ドラッグ&ドロップで複数画像をまとめてJPEGに変換
  • リサイズに対応
  • 新規フォルダにまとめて保存
  • 同名のファイルがある場合はリネーム
  • 設定をプリセットとして保存し切り替えられること
  • TouchBar対応


あとはここから膨らませていく感じで。

値を文字列と結合

値を文字列と結合したいときは
let a = 1
let result = "total " + String(a)
のように書くと、aから文字列が生成されるので文字列演算で結合できます。

よくあるコードなので簡単な書き方が用意されていて、
"total \(a)"
と、\()で値を囲むだけでもOKです。


インスタンスのキャストと同時に型チェック

インスタンスをダウンキャストするときに気になるのが型チェック。

Swiftではas?という便利なキャストが使えます。

?が付いていることから予想できる通りoptionalを返すため、キャストできないときはnilとなります。

オプショナルバインディングと組み合わせるのが良さそうですね。

if let a = b as? C {
    // bがCクラスのインスタンスにキャストできる場合に実行される
}

Touchdown 1.3公開!

Touchdown 1.3を公開しました。 ダウンロードは こちら から。 Ver.1.3の変更点は以下の通りです。 新機能 macOS Mojave 10.13以降に対応 ダークモードに対応 ぜひお試しください。