MacBook Proに長らく搭載されていて革新的でありながらイマイチ活用されていない機能といえばForce Touchトラックパッド。
実はTouchdownでも地味に対応しています。
ファイルをウィンドウにドラッグするとフォーカス枠が表示されますが、その際に指先にフィードバックが来るようにしています。
触覚フィードバックだけでなく、感圧を生かしたアプリも作ってみたいですね。
MacBook Proのための機能 その1
Touchdownを作ろうと思った最大のモチベーションは「MacBook Proをもっと活用したい」だったので、最初に注目したのはいうまでもなくTouch Bar。
まずはTouch Barに何か表示してみるところから始めて、プリセットの切り替えという無難な機能に落ち着きました。
しかし、ちょっと物足りないので、Touch Barジェスチャーにも対応し、ダブルタップでプリセットの選択と実行をまとめてできるようにしてみました。
なんでも表示できるのでもっと派手な感じにもしたかったのですが、Appleのガイドラインに従う限り、あまり目立つものは実装できないのが残念です。
まずはTouch Barに何か表示してみるところから始めて、プリセットの切り替えという無難な機能に落ち着きました。
しかし、ちょっと物足りないので、Touch Barジェスチャーにも対応し、ダブルタップでプリセットの選択と実行をまとめてできるようにしてみました。
なんでも表示できるのでもっと派手な感じにもしたかったのですが、Appleのガイドラインに従う限り、あまり目立つものは実装できないのが残念です。
Touch Barでダブルタップ検知
TouchdownではTouch Barの設定(NSSegmentedControl)をダブルタップで実行する機能を実装しています。
やり方は意外と簡単。
NSClickGestureRecognizerで検知したいジェスチャーを指定し、任意のControlに設定するだけです。
let gesture = NSClickGestureRecognizer() // NSClickGestureRecognizerのインスタンスを作成
gesture.target = self // 通知を受けるオブジェクトを設定
gesture.numberOfTouchesRequired = 1 // 検知する指の本数
gesture.numberOfClicksRequired = 2 // 検知するタップ回数
gesture.allowedTouchTypes = .direct // 指によるダイレクトタッチを検知
gesture.action = #selector(deferConvertFiles(_:)) // 通知を受けたときに実行するセレクタ
segmentedControl.addGestureRecognizer(gesture) // Controlにジェスチャーを設定
シンプルで強力な仕組みですね。
やり方は意外と簡単。
NSClickGestureRecognizerで検知したいジェスチャーを指定し、任意のControlに設定するだけです。
let gesture = NSClickGestureRecognizer() // NSClickGestureRecognizerのインスタンスを作成
gesture.target = self // 通知を受けるオブジェクトを設定
gesture.numberOfTouchesRequired = 1 // 検知する指の本数
gesture.numberOfClicksRequired = 2 // 検知するタップ回数
gesture.allowedTouchTypes = .direct // 指によるダイレクトタッチを検知
gesture.action = #selector(deferConvertFiles(_:)) // 通知を受けたときに実行するセレクタ
segmentedControl.addGestureRecognizer(gesture) // Controlにジェスチャーを設定
シンプルで強力な仕組みですね。
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:[])
なんらかの方法で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をリサイズ
意外と複雑な画像のリサイズ。
やり方は色々ありそうですが、今回使った方法を記録しておきます。
大まかな流れは
まずはあらかじめ準備した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)
やり方は色々ありそうですが、今回使った方法を記録しておきます。
大まかな流れは
- CGImageを準備
- CGContextを準備
- リサイズ後のサイズを指定
- contextを使って書き込む
- 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として使います。
まずNSTableViewとArrayControllerを2箇所バインド。
ContentとはarrangedObjects、Selection IndexesはselectionIndexes。

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

TableViewCellのBehaviorをEditableにしておきます。
基本的なとこだけど、何かと分かりにくいCocoa Binding。
iOSでは使えないらしく情報も少なめなので、簡単にまとめておきます。
ViewControllerにArrayを定義
NSTableViewは2箇所Bind
まずNSTableViewとArrayControllerを2箇所バインド。
ContentとはarrangedObjects、Selection IndexesはselectionIndexes。

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

値を変更できるようにする
TableViewCellのBehaviorをEditableにしておきます。

登録:
投稿 (Atom)
Touchdown 1.3公開!
Touchdown 1.3を公開しました。 ダウンロードは こちら から。 Ver.1.3の変更点は以下の通りです。 新機能 macOS Mojave 10.13以降に対応 ダークモードに対応 ぜひお試しください。
-
Touchdown 1.3を公開しました。 ダウンロードは こちら から。 Ver.1.3の変更点は以下の通りです。 新機能 macOS Mojave 10.13以降に対応 ダークモードに対応 ぜひお試しください。
-
インスタンスをダウンキャストするときに気になるのが型チェック。 Swiftではas?という便利なキャストが使えます。 ?が付いていることから予想できる通りoptionalを返すため、キャストできないときはnilとなります。 オプショナルバインディングと組み合わせるのが良さそうです...
-
Touchdown 1.2を公開しました。 ダウンロードは こちら から。 Ver.1.2の変更点は以下の通りです。 新機能 macOS High Sierra 10.13以降に対応 コマンドキーを押しながら起動することで設定を保存しない機能を追加 一...


