NSView でくっきりラインを引く

MacDev

アプリケーションを作っていると目盛りを描画する機会があるはず。作り方なんて考えなくてもラインを縦横に並べるだけで描くことができる、描画の中でも基本中の基本。

でもラインって意外にデリケートで、気をつけないとスクリーンのピクセルの格子と合わずアンチエイリアスでぼやけてしまう。普通にくっきりしたラインを引くだけでも意外に難しいのだ。それに、せっかくなら Retina ディスプレイの実力も引き出したい...

そんなビューの簡単なサンプルを作ったので参考にどうぞ。Retina ディスプレイでもそうでなくても、くっきり 1 px のラインが表示されるはず。

Result

→ 続きを読む

ベジェ曲線に沿ったグラデーション

MacDev

ベジェ曲線について調べているときに偶然見つけた記事。

グラデーションをベジェ曲線に沿って曲げながら描画するにはどうすればいいのかという話だ。図がたくさんあってとてもわかりやすい。

Before
After

方法は思ったよりシンプルで、要点を抜き出すと、

  1. ベジェ曲線を直線の集合に分解
  1. それぞれの直線に沿うように長方形を並べる
  2. それぞれの長方形にグラデーションを描画

...というだけの話である。(上の図は前述の記事から引用)

これを Cocoa でもできないものか。いや、特に使う予定はないのだけど、楽しそうなので...!

→ 続きを読む

その数字、ガタガタしてない? - AppKit で数字を等幅にする

MacDev

OS X 10.9-10.11 で連続してシステムフォントが変更された。

  • 10.9 で .Lucida Grande UI(ほとんど変化なし)
  • 10.10 で Helvetica Neue
  • 10.11 で San Francisco

このうち 10.11 で採用された San Francisco が現在も使われている。使い方に合わせてあらゆる部分がダイナミックに変化することによって使用シーンの幅を拡大し、今では Apple 製品全体でおなじみ(スクリーン上のみならず製品のロゴやキーボードの刻印にも使われるようになった)の存在である。

どこで使っても読みやすくスタイリッシュで万能感のあるこのフォントだけど、UI で使うときに一つ気をつけておきたいことがある。数字部分がプロポーショナルになったことだ。

数字は変化する頻度が高いのでこのまま使うと値が増減するたびに幅が変化して表示が左右にずれてしまうことになる。これに対処するためには

→ 続きを読む

2 つの Swift ライブラリを公開 - ZMAX、ZMIOUSB

MacDev

先日初参加した try! Swift で、ある人に「オープンソースやらないんですか?」「Mac 関係のコード見たい!」と言ってもらえた。これはいい機会。ちょうど誰かの役に立つかもしれないコードがいくつかあったので、熱のあるうちにライブラリとして GitHub で公開することに。ちょっと豪華な二本立て!

古くからある、特に macOS 限定の API に関してはまだ C 関数が多くて Swift からは触りづらかったりする。Swift 3 で Core Graphics 関係のオブジェクトが Swift っぽく触れるように改善されたのをヒントに、これらを触りやすくしてみようという試みである。

CGContextMoveToPoint(context, p1)
CGContextAddLineToPoint(context, p2)
CGContextStrokePath(context)
Swift 2
context.move(to: p1)
context.addLine(to: p2)
context.stroke()
Swift 3

→ 続きを読む

ちょっとした NSView の使い方

MacDev

AppKit はとにかく資料が少ない。世の中に出回っている情報は古く、最新情報は OS X の新バージョンが出るタイミングで発行される AppKit Release Notes ぐらいにしか書かれていなかったりするので、これを読んでいないとすぐに置いていかれてしまう。

今日は NSView の描画の話。「UIView みたいに backgroundColor プロパティがないから面倒だ」みたいな話をときどき見かけるけど、正しい方法を知っていればそこまで手間はかからない。

highlighted プロパティに応じて YES ならレッド、NO ならグレーの角丸矩形を描画する NSView サブクラスを考える。

@property (nonatomic, getter=isHighlighted) BOOL highlighted;

→ 続きを読む

知らずに回る Run Loop にご用心

MacDev

CotEditor 2.1.6 リリース!

以前記事を書いて紹介したように(→ 2015.2.19)何年も愛用している OS X 用プレーンテキストエディタである CotEditor。最新バージョンの 2.1.6 がリリースされた。

変更点を見てみると:

いくつか前のバージョンから保存時にフリーズするという問題があったのだけど、ほとんどの人はこれで直ると思います。

...という自慢でした。😤

-[NSTask waitUntilExit] の罠

自慢するだけなのもあれなので、今回の問題を引き起こしていた原因を同じバグで悩んでいる開発者のためにメモしておく。

CotEditor ではファイルの読み書きに authopen というコマンドを使うような構造になっているのだけど、それを Cocoa から呼び出すために NSTask というクラスが使われている。保存するデータをそこに投げたあと、処理を待つために呼ばれていたのが -waitUntilExit というメソッド。

waitUntilExit という名前の通り、アプリケーションの処理を休んでコマンドが終了するのを待ってくれるものかと思いきや、ヘッダファイルのコメントを読むと罠があった。

// poll the runLoop in defaultMode until task completes

Run Loop が回るらしい。...ということはこの待ち時間の間、先にセットされていたタイマーの時間が来たらその処理が次々と割り込んでくることになる!

CotEditor の場合、ファイルの保存で authopen が処理をしている中、その処理を待つ間に自動保存のタイマーが発動してしまい、予期せぬタイミングでもう一つの保存が実行されてしまっていた。それが結果的にフリーズを引き起こしていたのだ。

対策は簡単で、このメソッドを使わず単純なループで待つようにすればいい:

while (task.isRunning) {
    usleep(200);
}

Wait 処理を使うときはどんな仕組みで待っているのか注意しましょう。

Yosemite の NSViewController はこれまでとは違う!

MacDev

iOS App を作っている人なら、UIViewController が必要不可欠な存在であることがわかるはず。View(ビューとその子孫ビューのいくつか)を管理して、Model との関係をつなぎ、そのまとまり全体の処理を統括する Controller であり UIKit における MVC の柱と言える。

Mac ではどうだろうか。もともと AppKit では、1 つの NSWindowController ごとに MVC の関係を形成していた。View はウインドウであり、それに含まれる子孫ビューも含む。

ウインドウ単位で 1 Controller しか用意していないようではそのクラスがあまりに巨大化してしまう懸念があるけど、OS X 初期では処理ごとにウインドウが独立していることが多かったので、当時はそこまで不便ではなかった。ドロワーもシートもパネルもみんな独立したウインドウである。

OS X が進化するにつれ、Mac アプリケーションでは 1 つのウインドウ内にすべての機能を詰め込むようなデザインのものが多くなってきた。ドロワーは使われなくなったし、パネルで用意していたような機能もサイドバーとしてウインドウ内に配置されるようになってきた。こうなるともっと処理を分散させたい。そこで、OS X 10.5 Leopard において追加されたのが NSViewController というクラス。待望の、ビュー単位の Controller。これでアプリケーション開発が楽になるかと思いきや、本当に使い物にならなかった。

中身がなさすぎた

Leopard の時点で使い物にならないのはしかたがないとして、4 回もバージョンを重ねた OS X 10.9 Mavericks になっても NSViewController の中身はほとんど変化せず

  • -initWithNibName:bundle:
  • -loadView
  • .representedObject
  • .nibBundle
  • .nibName
  • .view
  • .title
  • -commitEditingWithDelegate:didCommitSelector:contextInfo:
  • -commitEditing
  • -discardEditing

たったこれだけ。ほとんど nib でビューを読み込んで包み込むだけ。ビューからの必要な通知も受け取れないし、レスポンダチェーンとしてアクションを受け取ることもできない。Cocoa のほかのクラスからも無視された状態であり、NSViewController とのつながりがあったのは OS X 10.7 Lion で追加された NSPopover と OS X 10.8 Mountain Lion で追加された NSPageController ぐらいだった。

この状況は OS X 10.10 Yosemite の登場で大きく変わることになる。手を入れるのが遅すぎると言いたくなるけど、今回は本気のようだ。便利なプロパティやメソッドが増えただけではなく、Cocoa アプリケーションを構成する一員としてしっかりと組み込まれた。今回は AppKit Release Notes for OS X v10.10 を読みながら、その変更点を紹介してみようと思う。

とは言っても、iOS の UIViewController とほぼ同等のものになったというだけの話である。

→ 続きを読む