NSToolbar でサイズが切り替わる設定画面のような WIndow を作る

下のようなXcode の設定ウィンドウのように NSToolbar が上部にあり、表示内容のサイズをアニメーション付きで変わるものを作ります。

f:id:soh335:20160907153338p:plain

storyboard

  • NSWindowController を用意します。
  • NSToolbar をセットし、下のように必要な NSToolbarItem を追加したりします。

f:id:soh335:20160907153518p:plain

  • NSWIndowController に紐付いている ViewController の view (NSView) を flipped = true を返すものにします。(理由は後述します。)

  • それぞれの設定画面の ViewController として作ります。必要な場合、レイアウトをします。

  • NSToolbarItem の action 先を紐付けます。

code

  • 上で紐付けると NSToolbarItem として event が来るので設定した identifier から必要な ViewController を生成します。
  • 生成した ViewController のサイズを計算し( autolayout している場合は fittingSize )、現在の View の高さとの差分をとります。(これも理由は後述します)
  • ContentViewController の childViewController から既存のものを削除し、生成した ViewController を追加し、view も追加します。
  • 生成した view の frame を指定します。(幅は既存の view, window と同じ、高さは上で計算したサイズの高さ, origin は 0, 0 で大丈夫です。これは上で flipped になっているからです。flipped でない場合は座標系が左下基準なので Window の下部に張り付いてアニメーションします)
  • NSWindow.setFrame(_:display:animate:) で animation 付きでサイズを指定します。これも上と同じ理由で、origin は現在の場所から上で計算した差分分を引きます。そうしないとサイズのみを変更した場合 Window の下部が固定されて上部が移動しますので不自然です*1

長くて文章にするとわかりづらいですがコードだとこういう感じです。

        let fittingSize = newVC.view.fittingSize
        
        let addition = fittingSize.height - self.contentViewController.view.frame.size.height
        
        if let oldVC = self.contentViewController?.childViewControllers.first {
            oldVC.removeFromParentViewController()
            oldVC.view.removeFromSuperview()
        }
        
        self.contentViewController?.addChildViewController(newVC)
        self.contentViewController?.view.addSubview(newVC.view)
        
        let windowFrame = self.window!.frame
        newVC.view.frame = NSMakeRect(0, 0, windowFrame.size.width, fittingSize.height)
        
        self.window!.setFrame(NSMakeRect(windowFrame.origin.x, windowFrame.origin.y - addition, windowFrame.size.width, windowFrame.size.height + addition ), display: true, animate: true)

みんなのGo言語【現場で使える実践テクニック】

著者の一人である fujiwara さんから本を頂きました。fujiwara さん、著者の皆様、技術評論社様ありがとうございます。

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

最初の本書で扱う内容にある通り、Go の文法等には触れられておらず、知っていると良い、便利、注意すべきことといった実践的な内容になっています。 全く Go を書いたことがない人でしたら、先に(この本でも紹介されていますが)A Tour of Go 等で入門してみるのが良いかと思います。

現在 Go を書いている人でしたら色々なことが書かれていますので、あの時うまく書けなかったけどこう書けば良かったのか、しっくり来なかったけどこれが Go のルールなんだなと発見があると思います。

書かれてる方も、songmu さん、mattn さん、fujiwara さん、deeeet さん、lestrrat さん、suzu_v さんと有名な方で丁寧に書かれています。とても安心して読めて、オススメです。 以下少しですが、各章の感想・紹介を書かせて頂きます。

1 章

songmu さんによる、Go の開発環境の構築から、開発をサポートするツールの紹介、Go らしいコードの書き方が紹介されています。 おそらく songmu さんの開発環境にかなり近いものを書かれていると思うので、なるほどこれは便利だなと思うものがあると思います。

2 章

mattn さんによるマルチプラットフォームで動くように注意すべき点がたくさん紹介されています。 大抵自分の環境( 私だと mac です ) で動けばいいようにまず書いてしまう気がします。なので他の環境(私にとっての windows)も含めて考えるとここにも注意しないといけないという、新しい発見がありました。

3 章

fujiwara さんによる実際のアプリケーションを書く際に使えるテクニックが紹介されています。 このように書くと、I/0, メモリの効率が良い、ユーザが使い易いなどから goroutine による非同期処理など様々のことが書かれています。実際に書いているものから抜粋されているので、参考にしやすいと思います。

4 章

deeeet さんによるコマンドラインツールを書く際の構成、標準パッケージの紹介、応用、その他のパッケージの選定等様々なことが紹介されています。 またメンテナンスしやすいようにはこのように書いていくと良いという内容もあり非常にためになります。

5 章

lestrrat さんによる reflect パッケージの使い方が書かれています。 reflect パッケージを使い倒す機会はなかなか少ないです。しかし必要な時は必要になるので、それに直面している方々にはとても参考になると思います。(2年前に読めていれば...)

6 章

suzu_v さんによる testing パッケージの紹介です。 Go のテストへのアプローチは少し他の言語と変わっていて、最近はあまり見ないですが少し前は違和感があるというエントリーを見かけた気がします。基本から、実際に使える色々なテクニックが紹介されているので、こちらも取り入れやすいかと思います。

42.195kmの科学 マラソン「つま先着地」vs「かかと着地」

「歳をとれば、それだけ注意が必要なんです。自分の肉体に耳を傾けなければなりません。私の肉体は精密な機械を積む自動車のようなものですから、メンテナンスをしなかったら、思うように動かすことはできません。速く走りたくても、走れないのです。だから、適切なメンテナンスが重要です。どんなに高価な自動車だって、修理と調整が必要でしょう?  こうした努力を怠れば、私は一〇年前に走るのをやめていたでしょう。事実、ほとんどの友人は一〇年前に走るのをやめてしまいました。普通の人なら年齢と共に筋力も衰えていきます。ですから、今でも生き残っているのは私だけですよ」

www.nhk.or.jp

この放送で取材した内容を本にしたものらしい。 born to run の後にオススメされてて、その中でもつま先着地の話があったので(あの本ではつま先着地が何なのかは詳しく書いてないので謎の走り方で気になっていた)読んでみた。

前回の本が物語だったけど、これはつい最近の話で科学科学した話でこれもこれでとても面白い。ただ、この映像を見る手段がないのと、図が本にないので所々理解が進まないところがあるのが少し残念。

BORN TO RUN 走るために生まれた ―ウルトラランナーVS人類最強の“走る民族”

「長い距離を走ってると」と彼女はつづけた。「人生で大切なのは、最後まで走りきることだけって気がしてくる。そのときだけは、わたしの頭もずっとこんがらがったりとかしていない。なにもかも静まりかえって、あるのは純粋な流れだけになる。わたしと動作とその動きだけ。それがわたしが愛するもの――ただ野蛮人になって、森を走ることがね」

「六四歳が一九歳と互角に渡り合う競技をほかに挙げてみてください。水泳? ボクシング? 接戦にもならない。われわれ人間にはじつに不思議なところがあります。持久走が得意なばかりか、きわめて長期間にわたって得意でいられる。われわれは走るためにつくられた機械――そして、その機械は疲れを知らないのです」  人は年をとるから走るのをやめるのではない、とディップシーの鬼はいつも言っていた。走るのをやめるから年をとるのだ……。

kindle unlimited に登録したときに目に入ったので読んでみた。原著も癖がある英語らしいけど、翻訳が自分には合わなくて最初の出だしが結構厳しかった(訳者の後書きも、自分の読む感じとは合わなかったから、翻訳がという感じではないかもしれない)

ただ、内容はすごい面白くて、謎の走る民族(タラウマラ族)を見つける話、シューズの話とか、人間がなぜ走るのかの話、最後に、副題の人とその走る民族のレースの話がある。

順序もよくて最後のレースに出る登場人物それぞれが何で走るのか、みたいなことを考えると熱い気持ちになる。

全然本と関係ないけど、調べると走る記録つけ始めたのが2年前ぐらいで、全くこのときは走れていない。(一回3キロとかが限界だった様子)

f:id:soh335:20160904121411p:plain

それがようやく一回 10 キロ、月 100 キロ少し越すぐらいまで走れるようになった。それもあってもう少し効率よく走れないものかとか、何で走るのか考えてたらさらに走る系の本を読むことになってる。

mac アプリに go のコードをリンクする

ios の場合は gomobile を使う必要がありますが、osx の場合は必要ないので go build -buildmode=c-archive したものをリンクすると mac アプリから呼ぶことが出来ます。

例えば適当に、 libdummy というディレクトリを作り main.go として以下のようなファイルを作ります。 c の世界に go の struct とかを持っていけないので、github.com/mattn/go-pointer を利用し、Save した結果 ( C.malloc(C.size_t(1)) の返り値 ) を c の世界に返します。

package main

import "C"

import (
    "github.com/mattn/go-pointer"
    "unsafe"
)

//export dummy
type dummy struct {
    i int
}

//export DummyCreate
func DummyCreate() unsafe.Pointer {
    return pointer.Save(&dummy{})
}

//export DummyDestroy
func DummyDestroy(p unsafe.Pointer) {
    pointer.Unref(p)
}

//export DummyAdd
func DummyAdd(p unsafe.Pointer, i C.int) {
    d := pointer.Restore(p).(*dummy)
    d.i += int(i)
}

//export DummyGet
func DummyGet(p unsafe.Pointer) C.int {
    return C.int(pointer.Restore(p).(*dummy).i)
}

func main() {}

$ go build -buildmode=c-archive すると、libdummy.a, libdummy.h が生成されるので、xcode 側でリンクするようにします。 ( Linked Frameworks and Libraries に libdummy.a を追加、 swift の場合は、Bridging Header で libdummy.h を include )

そうすると

        let d = DummyCreate()
        DummyAdd(d, 3)
        defer {
            DummyDestroy(d)
        }
        let i = "\(DummyGet(d))" // 3

こういう感じで呼べます。

GIF の color table を調べる

gif の color table を調べたくなったのでシュッと確認できるよう gifpalette というのを書きました。

github.com

実行すると

このように出力されます。 golang には gif の parser が標準でついてるので読むと color table がどこに入っているかとかがわかります。

true color で出力する

これを書くのに当たって調べていたらどうやらターミナルで true color が表示できる世界線に居ることを知ったので(いつの間に....) *1*2 golang で true color でターミナルに出力出来るものを書きました。それがこちら。

github.com

鮮やか...

Image I/O で大きいサイズの GIF が正しく作れない

golang から cocoa の話になりますが、Image I/O では CGImageDestinationCreateWithURL, CGImageDestinationAddImage を使うと gif が作れるけど、大きいサイズだと以下のように色が飛んでしまうことがありました。(正しくない画像の一部切り取ったもの)*3

f:id:soh335:20160831211959p:plain

なので gif ファイルの color table を調べるために上のものを作りましたが、調べた結果としては color table を外から入れたとしてもこのようになってしまうので Image I/O で大きい gif は厳しいなという感じです。

*1:https://gist.github.com/XVilka/8346728

*2:https://github.com/tmux/tmux/issues/34

*3: 大きくても飛ばないこともありました。ターミナルの背景のように同じ色が多く占めてる場合におかしくなる印象