cockscomblog?

cockscomb on hatena blog

watchOS 2のアーキテクチャが意味すること

以前に書いたように、watchOS 1.0においてWatchKitアプリはiPhoneの側でそのプロセスが動作する。Apple Watch側にあるのはUIリソースのみであり、実際の処理はiPhone上で行われる。これは非常に高度な仕組みでありながらも、パフォーマンス上の問題を抱えていた。watchOS 2.0からはこれが大きく変化して、いわゆるネイティブ化が行われ、実際の処理もApple Watch側で行われるようになる。とはいってもAPI上の変化は少なく、これまでとおおよそ同じようなインターフェースになっている上、ベースはiOSであるからUIKitの一部やFoundationが利用できる。

watchOS 2.0の正式なリリースは秋を予定しており、Apple Watchの発売からおおよそ半年である。しかしここで大きな疑問が残る。watchOS 1.0のアーキテクチャはいったい何のために用意されたのだろうか。ネイティブ化されるまでの半年間はいったい何のために必要だったのだろうか。Apple Watchに搭載されている一部のApple製のアプリは最初からネイティブで動作しているはずだが、はじめからその方法を解放できなかったのはなぜなのか。

Bitcode

iOS 9から加わる新しい仕組みの中に、App Thiningと呼ばれるものがある。アプリがユーザーの端末にダウンロードされる前に、App Storeがアプリに最適化を加えて余分なリソースを減らしたり、必ずしも必要とは限らないリソースを後からダウンロードさせたりといった仕組みである。その中に、Bitcodeと呼ばれる手法が存在する。

Bitcodeでは、デベロッパーはアプリの実行ファイルをLLVM Bitcode状態でApp Storeに(iTunes Connectに)アップロードする。LLVM BitcodeとはコンパイラであるLLVMの中間表現を格納するフォーマットで、Appleの独自の規格というわけではなく、LLVMプロジェクトの中で定められたフォーマットである。LLVMの中間表現は、このあと本来はLLVMによって最適化され、それぞれのプロセッサのアーキテクチャのネイティブコードに変換されるはずである。Bitcodeによる提出ではこの最終工程をApp Storeが行うので、将来さらに高度な最適化ができるようになったり、あるいはプロセッサのアーキテクチャが変わったときにも、デベロッパーによるバイナリの再提出なしに対応できるという寸法である。

Bitcodeでの提出は、iOSアプリではいまのところ任意、watchOSのネイティブアプリでは必須とされている。

Apple Watchのプロセッサ

現在のApple WatchはApple S1というSiPである。Chipworksによれば、内蔵されているプロセッサにはAPL0778と書かれているようだ。Appleはこのプロセッサのアーキテクチャを公表していない。

総合的に見て、AppleApple Watchのアーキテクチャを流動的に保とうとしているように思われる。アプリのネイティブ化はBitcodeの導入を待っていたように感じられるし、Bitcodeがあればアーキテクチャに縛られることがない。

ウェアラブルデバイスにおける共通の問題はバッテリーの保ちである。Appleがこれに対して今後どのようなアプローチを取ってくるのか目が離せない。

Apple Watchを買うべきか

ひとより先に未来に辿り着けるのだとしたら、買わない理由がどこにあるのだろう。

Swift 1.2

第61回 Cocoa勉強会関西で“Swift 1.2 The long-awaited language updates”と題して発表した、Swift 1.2の主だった(おもしろい)変更点の紹介です。

if let

Swift 1.2で最も改善されたのはif文です。if letでOptionalをunwrapできる機能が大きく向上し、複数のOptionalを同時にunwrapできるほか、unwrapされた値について条件を加えることができるようになりました。

例えばcondition: Boolが真でふたつのOptional<Int>nilではなく、大小関係にも条件がある、という条件を表してみます。

Swift 1.1

let condition = true
let aNumber: Int? = 3
let anotherNumber: Int? = 7
if condition {
    if let a = aNumber {
        if let b = anotherNumber {
            if a < b {
                println(a + b)
            }
        }
    }
}

Swift 1.2

let condition = true
let aNumber: Int? = 3
let anotherNumber: Int? = 7
if condition, let a = aNumber, let b = anotherNumber where a < b {
        println(a + b)
}

極端な例ではありますが、Swift 1.1ではifを4回重ねていたものを、Swift 1.2では1つのifで表現できます。

let constant must be initialized before use

let定数は再代入できません。Swift 1.1ではそのために、宣言と同時に値を決める必要がありました。Swift 1.2ではその制限が少し緩和され、値が使われる前に確実に初期化されるなら、宣言の後で値が決まってもよいことになりました。

Swift 1.1

let hour = NSCalendar.currentCalendar().component(.CalendarUnitHour, fromDate: NSDate())

var meridiem: String
if hour < 12 {
    meridiem = "AM"
} else {
    meridiem = "PM"
}

println(meridiem)

Swift 1.2

let hour = NSCalendar.currentCalendar().component(.CalendarUnitHour, fromDate: NSDate())

let meridiem: String
if hour < 12 {
    meridiem = "AM"
} else {
    meridiem = "PM"
}

println(meridiem)

Swift 1.1ではvarにしなければならなかったmeridiemが、Swift 1.2ではletにできます。ifに限らずswitchなども利用できますが、全ての条件で初期化されることが保証されている必要があります。

Set

CollectionTypeArrayDictionaryなどに加えてSetが追加されました。Setは集合を表し、Foundation.frameworkのNSSetに対応します。Setの導入に合わせて、Objective-CでNSSetを利用していたAPISwiftから利用する場合にはSwiftのSetを使うことになります。

Swift 1.1

let abc: NSSet = NSSet(objects: "A", "B", "C")
if NSSet(objects: "A").isSubsetOfSet(abc) {
    println(abc)
}

Swift 1.2

let abc: Set<String> = ["A", "B", "C"]
if abc.isSupersetOf(["A"]) {
    println(abc)
}

NSSetと違って内包する値の型を指定でき、またArrayリテラルで表現できます。

Static methods and properties

Swift 1.2からclassにもstaticな関数やプロパティを持てるようになりました。これまでもclass funcがありましたが、static funcclassかつfinalという意味になります。またstatic letで宣言した定数は最初にアクセスされた時点で評価されるという特徴を持ちます。

class StaticPropertiesAndMethods {
    static func printDate() {
        println(date)
    }
    static let date = NSDate()
}

StaticPropertiesAndMethods.printDate() // => "2015-04-10 07:01:00 +0000"

クラス変数はObjective-Cには存在せず代替的な手法で実現していましたが、Swiftでついに用意された文法と言えます。

Non-Void return types in Void contexts

Swift 1.1でクロージャを使うときに、返り値としてVoidを要求するところに1行だけ、Voidではない値を返す式があると、コンパイルエラーになっていました。これはクロージャが1行だけの式を持つとき、その式の結果がクロージャ全体の返り値として取り扱われ、型がミスマッチだと判定される結果です。このために無意味なreturnを付け加えるなどして回避していました。

Swift 1.2ではこのようなときにVoidではない値が返っても許容されるようになりました。これで意味上は不要なreturnを書く必要がなくなり、すっきりします。

Swift 1.1

let wantsVoid: (() -> Void) = {
    "non-Void here"
    return
}

Swift 1.2

let wantsVoid: (() -> Void) = {
    "non-Void here"
}

zip

Swift 1.2ではzip関数が導入されました。ふたつのSequenceTypeの組み合わせを簡単に作ることができます。要素数が異なる場合には短い方に合わせられます。

let ordinals = ["first", "second", "third"]
let values = [1, 2, 3]

var ordinalsDict: [String: Int] = [:]

for (key, value) in zip(ordinals, values) {
    ordinalsDict[key] = value
}

println(ordinalsDict) // => ["first": 1, "second": 2, "third": 3]

Type casting

キャスト関連でも非互換な変更などがあります。

let any: AnyObject = 3
let number: Int = any as! Int

危険な操作であるときの一貫性のため、ダウンキャストするときas!を使うことになりました。

protocol SomeProtocol {
    func something() -> String
}

class SomeClass: SomeProtocol {
    func something() -> String {
        return "Something awesome"
    }
}

let some: AnyObject = SomeClass()
if some is SomeProtocol {
    println((some as! SomeProtocol).something())
}

@objcではない通常のprotocolにもキャスト関連の操作ができるようになっています。

@noescape

関数が引数としてクロージャを受け取るとき、クロージャの内部で利用される変数はキャプチャされ、それぞれ強参照された状態となります。またこのためにクロージャの外のオブジェクトのプロパティなどを利用する場合にはself.で修飾する必要があります。これによってクロージャを実際に評価するタイミングはいつでもよいことになっています。

しかしクロージャを受け取ってすぐに評価することが決まっている関数の場合はどうでしょう。そういうときはキャプチャする必要が無いし、self.も不要です。キャプチャしない方がパフォーマンス上でも有利でしょうし、最適化しやすいかもしれません。このようにすぐに評価されることがわかっているクロージャのために追加された新しいアノテーション@noescapeです。

func map<U>(f: @noescape (T) -> U) -> U?

Optionalmapでは上記のように@noescapeされています。このためクロージャで使う変数がプロパティであってもself.は不要です。

このようにSwiftではこれらの挙動をescapeという言葉で表しています。

func map<U>(transform: (T) -> U) -> [U]

ところでArraymapを見てみると、上記のように@noescapeアノテーションがないことが分かります。一貫性がないように思われるかもしれませんが、これには理由があるように思えます。lazy()関数などを用いて遅延評価する場合にはescapeせざるを得ません。これは簡単な実験で確かめられます。

let start = NSDate()
let array = Array(0..<3)

let result = lazy(array).map { (number: Int) -> Int in sleep(1); return 100 * number }

println(NSDate().timeIntervalSinceDate(start))

このようにlazy()関数を使えばmap中でsleep(1)していても一瞬で実行が終わり、result[2]などresultの要素にアクセスしたとき初めてmapクロージャが評価されます。このlazy()を消すと、このスニペットは少なくとも3秒の実行時間を必要とします。

これがArray.mapがescapeを必要とする理由です。

この@noescapeが追加されたのに合わせてデフォルトでは@autoclosureもescapeされなくなりました。escapeしてほしい場合には@autoclosure(escaping)と書く必要があります。

flatMap

新しいflatMap関数を使うとmapではできないちょっとしたことが簡単にできるようになります。

let mapped = ["A B C", "D E", "F"].map {
    $0.componentsSeparatedByString(" ")
}
println(mapped) // => [[A, B, C], [D, E], [F]]

let flatMapped = ["A B C", "D E", "F"].flatMap {
    $0.componentsSeparatedByString(" ")
}
println(flatMapped) // => [A, B, C, D, E, F]

上記の例のように、Arrayの要素に何らかの変換を行うとき、flatMapなら要素毎に新たなArrayを返しても2次のArrayになりません。

func toInt(value: String) -> Int? {
    return value.toInt()
}

let one: String? = "1"

let mapOne = one.map { toInt($0) }
println(mapOne) // => Optional(Optional(1))

let flatMapOne = one.flatMap { toInt($0) }
println(flatMapOne) // => Optional(1)

また上記の例では、OptionalmapflatMapの違いを示しています。mapの中でさらにOptionalな値を返すとOptional<Optional<Int>>を得ることになります。flatMapではOptional<Int>です。これは非常に有用であることがわかるでしょう。

このようにflatMapmapとよく似ていますが、最初のArrayと要素数が変わったArrayが返ったり、多重のOptionalを避けられたりといった特徴があります。

let intLike = ["12.3", "45", "6-7-8"]
let results = intLike.flatMap {
    $0.toInt().map { [ $0 ] } ?? []
}

println(results) // => [45]

上記はこれの応用例で、ふつうのmapであればArray<Int?>が返るところを、nilを除去してArray<Int>にしています。

NSEnumerator.generate() -> NSFastGenerator

Swift 1.2ではFoundation.frameworkにも少し拡張が増えています。

let fonts = "/System/Library/Fonts"
let files = NSFileManager().enumeratorAtPath(fonts)!
for path in files {
    println(path)
}

enumeratorAtPathNSDirectoryEnumeratorというNSEnumeratorのサブクラスを返します。Swift 1.2より前では、NSEnumeratorSequenceTypeではなく、for inが使えませんでした。Swift 1.2からはgenerator()メソッドが加わり、protocol SequenceTypeを満たすようになったためfor inできるようになりました。

Swiftはこのように、Foundation.frameworkについてSwiftから利用しやすいように多少の拡張を加えており、今後もこれらが拡充されていく可能性があります。

その他の非互換な変更

Remove implicit conversions from NSString/NSArray/NSDictionary to String/Array/Dictionary

NSStringなどのFoundation.frameworkのオブジェクトから対応するSwiftStringなどへの暗黙的な変換が行われなくなりました。明示的にas Stringなどと書いてキャストしておく必要があります。逆のStringからNSStringといった変換はこれまで通り暗黙的に行われます。

SwiftからObjective-Cのヘッダを見たときにはNSStringStringになっており、Swiftからはほとんど常にStringを利用していることになるので、実際的にこれが問題になるケースは少ないでしょう。ただしCoreFoundation.frameworkに関するオブジェクトを利用するときは別です。

countElements → count

素数を数える関数の名前が変わりました。分かりやすいですね。

パフォーマンスに関する変更

Swift 1.2と共に様々なパフォーマンスが改善されました。Swiftで書かれたプログラムのパフォーマンスが改善されたほかにも興味深い変更があります。

Incremental build

Swiftコンパイルがインクリメンタルになりました。これまでは、ひとつのファイルを変更したときにも全てのファイルのコンパイルをやり直す必要がありましたが、これからは必要最低限のファイルをコンパイルし直すだけでよくなりました。ただし今後も、変更したファイルだけをコンパイルし直すのではなく、変更が影響するであろうファイル全てをコンパイルし直す必要があります。

Swift 1.2でも思ったよりコンパイルが遅いということがあるかもしれませんが、もしかするとXcode 6.3.1の問題かもしれません。

Whole Module Optimization

オブジェクト指向パラダイムにおいて、クラスを継承してメソッドをオーバーライドするというのがあります。すなわち、メソッドを呼び出す際にはオーバーライドされた後の実装を呼び出す必要があるということです。プログラムの実行中、メソッドの呼び出しやプロパティを操作する時に、実際に呼び出すべき対象を動的に解決します。これをdynamic dicpatchと呼んでいます。

ここで、例えばfinal修飾されているメソッドは、ぜったいにオーバーライドできません。そういった場合にはdynamic dispatchする必要がないので、Swiftコンパイラは静的に解決してくれます。dynamic dispatchが行われない分、実行時のパフォーマンスは改善します。

さらにアクセス制御のprivateで修飾されていれば、そのファイルの外からはオーバーライドできません。つまりそのファイル中でオーバーライドされているか検査することで不要なdynamic dispatchをしないようにコンパイルできます。private修飾はパフォーマンスにも役立っていることがわかります。

さらにinternalでもこれを可能にするのがWhole Module Optimizationです。internalで修飾されていればそのモジュールの外からオーバーライドされません。つまり、モジュール全体を検査することで、不要なdynamic dispatchを避けることができます。この最適化は比較的時間がかかるため、現在のところオプションになっています。

これらの話題はSwiftのブログにも書かれています。

Objective-CのNullability

Swift 1.2に合わせてObjective-Cにも変更があります。変数や引数、返り値などがnilを取り得るかをアノテートできるようになりました。nullablenonnullで修飾することでこれが可能です。また全てにこれらのアノテーションを付けて回るのは現実的ではないので、NS_ASSUME_NONNULL_BEGINNS_ASSUME_NONNULL_ENDで囲われている部分はnonnullということになっています。基本的にはこれで囲んでおいて、nullableだけ明示的に書くのがよいでしょう。

これらのアノテーションを書いておくと、Swiftから見たときにImplicitly Unwrapped OptionalではなくOptionalかそうでないかはっきりします。さらにObjective-Cにおいても、nonnullとアノテートされたポインタにnilを入れようとしていれば静的解析で検出されます。ただし実行時には影響しません。


Swiftはまだ変わり続ける生きた言語です。Swift 1.2はSwiftの正式なリリース以来もっとも大きな変化で、開発者にとっても非常に歓迎すべき改善が数多くあります。今後のSwiftの進化に期待しながら、Swift 1.2でバリバリ開発していきたいですね。

詳解 Swift

詳解 Swift


いっしょにSwiftでバリバリ開発しませんか。

iOS/OS X 用ライブラリ UTIKit を OSS でリリースしました

UTI

UTI とは Uniform Type Identifier のことで、iOSOS X の世界で、ファイルなどの種類を表すために用いられる文字列です。

UTI の実例として、画像一般を表す public.image や、そのサブタイプで JPEG 画像を表す public.jpeg などがあります。iOSOS X で利用されるほとんどのファイル形式には対応する UTI があり、例えば Keynote ファイルは com.apple.iwork.keynote.key などとなります。もちろんサードパーティも例外ではなく、Adobe Photoshop のファイルは com.adobe.photoshop-image となり、Microsoft Word の docxorg.openxmlformats.wordprocessingml.document です。

iOSOS Xフレームワークは、ファイルの種類を表すのにこの UTI を利用することが多くあります。例えば Photos.framework から画像データを得たいというとき、PHImageManagerrequestImageDataForAsset(_:options:resultHandler:) メソッドを利用します。この resultHandler((NSData!, String!, UIImageOrientation, [NSObject : AnyObject]!) -> Void)! という型のクロージャで、クロージャの第2引数はドキュメントを読むと dataUTI となっています。すなわち、画像データの種類が UTI として得られます。

ところで、この UTI から拡張子MIME type や拡張子を取得するにはどうすればよいのでしょうか。画像をサーバーにアップロードするときには MIME type が必要かもしれません。例えば public.jpeg だったら image/jpeg という風な対応を丁寧に処理しても良いかもしれませんが、そもそも iOSOS X にはそういった関数が備わっています。func UTTypeCopyPreferredTagWithClass(inUTI: CFString!, inTagClass: CFString!) -> Unmanaged<CFString>! を利用することで UTI から MIME type や拡張子が得られます。

let mime = UTTypeCopyPreferredTagWithClass("public.jpeg", kUTTagClassMIMEType).takeRetainedValue() // => "image/jpeg"

let ext = UTTypeCopyPreferredTagWithClass("public.jpeg", kUTTagClassFilenameExtension).takeRetainedValue() // => "jpeg"

またここでは紹介しませんが、逆に MIME type や拡張子から UTI を得る関数もあります。非常に便利ですが、ごくたまにしか使わないので忘れがちです。筆者は毎回のように忘れて、過去のプロジェクトや IRC のログを検索して思い出します。

UTIKit

ということで、このような UTI に関連した処理を利用しやすくするラッパーライブラリが、UTIKit です。これを利用すると上記の処理が以下のようになります。

UTI("public.jpeg").MIMEType // => "image/jpeg"

UTI("public.jpeg").filenameExtension // => "jpeg"

ごく短くなる上に、インターフェースもわかりやすいですね。

もちろん MIME type や拡張子から UTI を得るのも簡単です。

let jpeg = UTI(MIMEType: "image/jpeg")

let jpeg = UTI(filenameExtension: "jpeg")

Equatable を実装しているためこれらふたつは同値です。

UTI(MIMEType: "image/jpeg") == UTI(filenameExtension: "jpeg") // => true

また、public.jpegpublic.image に従っているので、switch を利用して以下のように書けます。

switch UTI("public.jpeg") {
case UTI("public.image"):
    println("JPEG is a kind of images")
default:
    fatalError("JPEG must be a image")
}

これらのように、UTI が Swift ライクに扱えることがわかります。

ご利用方法

CocoaPods 0.36 以降でインストールできます。Podfile に以下の行を書き足しましょう。

pod "UTIKit"

また Carthage でもインストールできるかもしれません(試してない)。

MIT ライセンスです。どうぞご利用ください。

To Do

NSWindowのタイトルバーをカスタマイズする

人類の多年の夢であったNSWindowのタイトルバーのカスタマイズであるが、YosemiteになってついにオフィシャルなAPIが実装された。


YosemiteではApple製のアプリケーションほとんどにおいてデザインが一新され、かつてOS Xを特徴付けていたツルッとしたテクスチャは取り払われた。そしてウインドウのタイトルバーまでもが変わった。

f:id:cockscomb:20150208004605p:plain
タイトルバー

ウインドウのタイトルバーは、OS XにおいてGUIアプリケーションの要である。タイトルバー自体がウインドウを移動するためのノブであり、左にはウインドウを操作するための赤・黄・青のコントロールが、中央にはウインドウのタイトルが表示される。ときにはツールバーと見かけ上くっついたりするが、いずれにしてもウインドウの中心はタイトルバーである。

f:id:cockscomb:20150208005228p:plain
Safariのタイトルバーにはツールバーが表示されている

そのタイトルバーをカスタマイズしたい、これは全人類が生まれながらに持っている当然の欲求である。そして、しかし、Appleは長年にわたってそういったAPIを提供してこなかった。ウインドウをウインドウたらしめるタイトルバーを、心ないデベロッパーが欲望のままに毀損することを許さないというように、禁断の果実を決して手放そうとはしなかった。それがYosemiteになって、Apple製のアプリケーションでさえ、一般的なタイトルバーのルールを破壊した。いまやタイトルバーには信号機のようなコントロールを残して、タイトルの代わりにツールバーが表示されたり、あるいはウインドウのコンテンツがそのまま表示されるようになった。これはいまやAppleだけの特権ではなく、ついにAPIが提供されたのである。

f:id:cockscomb:20150208005317p:plain
リマインダーのタイトルバーはコンテンツと重なる

タイトルを非表示にする

OS X 10.10 YosemiteからNSWindowに新しく追加されたプロパティvar titleVisibility: NSWindowTitleVisibilityを使うと、ウインドウのタイトルを非表示にできる。enum NSWindowTitleVisibilityは以下のように二つの値からなる。

enum NSWindowTitleVisibility : Int {
    case Visible
    case Hidden
}

デフォルトは.Visibleであるが、ここでヘッダから.Hiddenのコメントを引用する。

The always hidden mode hides the title and moves the toolbar up into the area previously occupied by the title.

NSToolbarがある場合にはタイトルがあった場所に移動するようだ。これによって、例えばSafariやカレンダーのように、タイトルバーの高さが少し高く、信号機のようなコントロールのすぐ右にツールバーがあるようなウインドウが作れる。NSWindowNSToolbarを設定して、あとは以下のようなコードをNSWindowControllerfunc windowDidLoad()などに実装する。

window?.titleVisibility = .Hidden

f:id:cockscomb:20150208005920p:plain
単にツールバーを置いたウインドウ

f:id:cockscomb:20150208005928p:plain
ツールバーを置いてtitleVisibility = .Hiddenとしたウインドウ

タイトルバーを装飾する

OS X 10.10 YosemiteではNSViewControllerが大幅に強化され、iOSUIViewControllerから多くの思想を受け継いだ。そして同時にNSViewControllerのサブクラスがいくつか追加された。その一つがNSTitlebarAccessoryViewControllerである。

NSTitlebarAccessoryViewControllerは、その名前が示す通り、タイトルバーを装飾するViewのControllerである。このViewControllerのviewプロパティが返すViewが、NSWindowのタイトルバーに表示される。合わせてNSWindowにも以下のようなプロパティやメソッドが追加されている。

var titlebarAccessoryViewControllers: [AnyObject]
func addTitlebarAccessoryViewController(childViewController: NSTitlebarAccessoryViewController)
func insertTitlebarAccessoryViewController(childViewController: NSTitlebarAccessoryViewController, atIndex index: Int)
func removeTitlebarAccessoryViewControllerAtIndex(index: Int)

NSWindow複数NSTitlebarAccessoryViewControllerを順序付きで管理する。タイトルバーの中でどの部分に表示されるのかは、NSTitlebarAccessoryViewControllerの持つvar layoutAttribute: NSLayoutAttributeというプロパティで決定される。ただし現在の所NSLayoutAttributeのうち有効な値は.Bottom.Rightのみであることがヘッダに書かれている。もちろんそれぞれに対応する位置は、タイトルバーの下と右である。

実際に表示するには以下のようなコードを、例えばNSWindowControllerfunc windowDidLoad()に実装する。NSTitlebarAccessoryViewControllerインスタンスはStoryboardから生成している。

let accessory = storyboard?.instantiateControllerWithIdentifier("TitlebarAccessory") as NSTitlebarAccessoryViewController
accessory.layoutAttribute = .Right
window?.addTitlebarAccessoryViewController(accessory)

f:id:cockscomb:20150208010129p:plain
layoutAttribute = .BottomNSTitlebarAccessoryViewControllerを設定したウインドウ

f:id:cockscomb:20150208010116p:plain
layoutAttribute = .RightNSTitlebarAccessoryViewControllerを設定したウインドウ

コンテンツをタイトルバーの領域まで拡げる

タイトルバーの位置までウインドウのコンテンツを表示するために、新たにvar titlebarAppearsTransparent: Boolプロパティと、NSFullSizeContentViewWindowMask定数が用意された。

titlebarAppearsTransparentはタイトルバーの背景を透明にするかどうかを表す。NSFullSizeContentViewWindowMaskは、NSWindowvar styleMask: Intプロパティに利用する定数で、タイトルバーの領域までをコンテンツの領域とすることを表現するものである。

NSFullSizeContentViewWindowMaskNSScrollViewの組み合わせによって、タイトルバーの下にコンテンツが透けるような表現が可能になる。またNSFullSizeContentViewWindowMaskを設定した上でtitlebarAppearsTransparentを真にすると、信号のようなコントロールだけのウインドウが作れるのだ。

window?.titleVisibility = .Hidden
window?.styleMask |= NSFullSizeContentViewWindowMask
window?.titlebarAppearsTransparent = true

f:id:cockscomb:20150208010259p:plain
タイトルバーの部分までNSSplitViewのコンテンツを表示させるようにしたウインドウ

伝統的手法

有史以来、NSWindowのタイトルバーをカスタマイズすると言えば大きく二つの方法があり、ひとつはウインドウのコントロールまで完全に自作して自由に表示する方法、そしてもうひとつはwindow.contentView.superviewという風にNSWindowの実装に依存してビューの階層を辿る方法である。現実的には主に後者が利用されることが多かったのではないだろうか。(NSThemeFrameなどで検索するとそういった方法を指し示すページが今も見つかるだろう。)

しかしOS X 10.10 Yosemiteでは、このビューの階層を辿る方法を使っていると"NSWindow warning: adding an unknown subview:"というログが出力されるようになり、代わりにオフィシャルな方法が提供されることになった。このことはAppKitのリリースノートに記載されている。

まとめ

ここまでで、OS X 10.10 Yosemiteから追加されたAPIによって、柔軟にタイトルバーをカスタマイズできることがわかった。これらはもちろん組み合わせることもできる。例えばSafariでは、titlebarAppearsTransparent = trueとした上でlayoutAttribute = .BottomNSTitlebarAccessoryViewControllerを設定しているかもしれない。

f:id:cockscomb:20150208010722p:plain
SafariのようにtitlebarAppearsTransparent = trueかつlayoutAttribute = .BottomNSTitlebarAccessoryViewControllerを設定したウインドウ

また昨今噂されるUXKitでは、UXNavigationControllerなどUIKit由来の機能が存在するらしい。これらのNSWindowの新しいAPIを使って、例えばUINavigationBar相当の機能をタイトルバーに押し込めることもできるのではないだろうか。

まだ見ぬOS X 10.11への期待を膨らませつつ、読者諸氏もNSWindowのタイトルバーをカスタマイズしてみてほしい。こちらからは以上だ。


詳解 Swift

詳解 Swift

砦を破壊する技術

みんな楽しそうに遊んでいてけまらしいので、Besiege買った。僕だって物理エンジンの世界で城砦を破壊してみたい。

古来より城攻めには投石器と相場は決まっており、かの芭蕉も「夏草や 兵どもが 夢のあと」という句で投石器の威力を詠っている。

f:id:cockscomb:20150205222218p:plain
見よ、これが投石機の雄姿である

どうせ一度きりの人生、ただの石ではなく爆弾を発射したいものである。

f:id:cockscomb:20150205222223p:plain
投石に失敗すると自爆する……

物理エンジンだけあって、現実世界と同じように失敗したりもする。人生に筋書きは存在しない。

f:id:cockscomb:20150205222227p:plain
美しい投石フォーム

画像だけでは伝わりづらいかもしれませんので動画を撮りました。

おもしろいので皆さんもどうぞ。

Androidアプリ開発を始めるときに読む本

今年はついにAndroidアプリを作りました。Androidアプリを開発するに当たって、Java言語にもAndroidプラットフォームにも明るくなかったので、勉強しようと本を読みました。いろいろ読みましたが、そのうち特によかったものを紹介します。

Java言語

Effective Java

Javaをちゃんと書けるようにしようと思って読みました。JavaのSerializableやClonableの挙動について知識を得られたし、Javaの言語上の特性を意識した設計について一定の視座を得られたように思います。読まずにJavaを書けていたとは思えないので、大変有意義でした。『並行性』の章などはまた再読したいと思います。

EFFECTIVE JAVA 第2版 (The Java Series)

EFFECTIVE JAVA 第2版 (The Java Series)

Android

プロの力が身につくAndroidプログラミングの教科書

ActivityやServiceにContentProvider、レイアウト、テストなど、一通り書いてあります。おおよそ網羅的で、どのような要素があるのかを最初に把握できます。これはアプリの設計においてなるべく正しい手段を選択するために必要なことだと思います。とにかく一通り書いてあるというのがとても良くて、なかなか他にはありませんでした。Androidアプリの開発を学ぶときに最初に手に取るべきは本書だと思います。

プロの力が身につく Androidプログラミングの教科書

プロの力が身につく Androidプログラミングの教科書

Master of Fragment

Fragmentのライフサイクルや復元、Support LibraryのFragmentについて、あるいはアニメーションについてなど、Fragmentについて学ぶべきほとんどのことが書いてあります。AndroidではFragmentでけっこうハマると思うので、Fragmentを活用したいならこれを読むとよいでしょう。

達人出版会でベータ版が買えるという状態ですが、いろいろなフォーマットに対応しているので不便はないと思います。

Master of Fragment (Android Professional Developerシリーズ)
あんざいゆき, わかめまさひろ
達人出版会
発行日: 2014-02-14
対応フォーマット: PDF, EPUB

Android Security 安全なアプリケーションを作成するために

AndroidiOSより自由度が高く、アプリ間の連携の仕組みもリッチですが、その副作用としてセキュアじゃないアプリを簡単に作れてしまいます。本書はAndroidの基本的なセキュリティの仕組みから解説されていて、とても役立ちます。Androidアプリを作るときには必ず理解しておきたい内容が書かれています。

Android Security  安全なアプリケーションを作成するために

Android Security  安全なアプリケーションを作成するために

Androidアプリ開発のセキュリティについては『Androidアプリのセキュア設計・セキュアコーディングガイド』も役に立ちます。

まとめ

Androidアプリ開発に入門するには、大体上記の4冊くらいを読めばよいと思います。あとはGoogleのドキュメントを眺めるとか、APIのdiffを全部見るとか、最新のイケてるライブラリを調べるとかするのがよいでしょう。技術の進歩が早い分野ですから、絶えず知識をアップデートしていく必要があります。

これから年末年始で、退屈凌ぎにAndroidアプリ開発を学ぼうという皆さんのお役に立てたらと思います。