イントロ
NSAttributedStringで表示テキストへ装飾しようと思うと、以下の様な記述をすると思います。
let label = UILabel(frame: CGRectMake(0,0,100,50))
let str1 = NSMutableAttributedString(
string: "Hoge",
attributes: [
NSForegroundColorAttributeName: UIColor.redColor(),
NSFontAttributeName: UIFont.systemFontOfSize(14)
]
)
let str2 = NSMutableAttributedString(
string: "Fuga",
attributes: [
NSForegroundColorAttributeName: UIColor.blueColor(),
NSFontAttributeName: UIFont(name: "Helvetica", size: 17)!
]
)
str1.appendAttributedString(str2)
label.attributedText = str1
この記述方法に対して、いくつかやりにくさを感じていました。
- attributesとして設定できるものが[String: AnyObject]なので、定数名と値として設定できる型を覚えておくなり調べるなりする必要がある
- 単純にUILabelやUITextViewへテキストを入れるだけに比べ、やたらコード量が膨れ上がる
- あとから読み返したり人が書いたコードを読んだ時、どう表示されるのかすぐにイメージ出来ない
仕事でWebサイトっぽい装飾のUIを求められやすいのですが、そんな時は大体NSAttributedStringを使うようにしています。結構よく使うにもかかわらずずっと苦手意識を持っており、「できるだけ使いたくないなぁ...」と思いながら渋々コードを書いてきました。この憂鬱な気持ちから開放されるために、NSAttributeStringを生成するためのライブラリを開発しました。
使い方
同じテキスト表示をこのライブラリで行うと以下のようになります。
let label = UILabel(frame: CGRectMake(0,0,100,50))
label.attributedText = "Hoge".stylize().color(.redColor) .size(14).attr +
"Fuga".stylize().color(.blueColor).size(17).font(.Helvetica).attr
メソッドチェーンで文字列に対してスタイルを適用していき、最終的に"attr"を呼ぶとNSAttributedStringへ変換されます。NSAttributedStringを"+"で連結できるようしているため、異なるスタイルが適用された文字列を足していく感じで書けます。
範囲指定もできるようにしており、さきほどの文字列は
let label = UILabel(frame: CGRectMake(0,0,100,50))
label.attributedText = "HogeFuga".stylize()
.range(0..<4) .color(.redColor) .size(14)
.range(4..<UInt.max).color(.blueColor).size(17).font(.Helvetica)
.attr
とも書けます。
特徴
- メソッド名と引数によって、どんな属性が指定できて値には何を入れたらいいかはっきりする
- メソッドチェーンを使い、思考の流れに沿って線形的にスタイルを指定していけるので書きやすい
- 文字列と属性をNSAttributedStringへ渡すのではなく、文字列に対して属性を適用していくよう書けるため読みやすい
実装
インターフェース部分は基本的にはBuilder Pattern(Effective Java versionとか言われているほう)で作っています。
また、このクラスでは状態の違いによって振る舞いを変えたかったのですが
、その時の参考にPhantom Typeという手法を利用しました。
- 範囲選択をしたらその後必ずひとつ以上のAttributeの適用を行う
- Attributeの適用後はNSAttributedStringへの変換や新たな範囲選択ができる
@taketo1024さんの「Swift で Phantom Type (幽霊型)」がなんか使えそうだと思って、型パラメータの違いによって呼べるメソッドを出し分ける、という実装にしました。
はじめに必ずStringへstylize()を呼ぶようにしているのは、一旦こちらで用意したクラスへ変換した上で、その中に用意された色々なメソッドを呼べるようにしたかったためです。また、最終的にNSAttributedStringを作るためには明示的にattrを呼ぶ必要があるようにしました。あくまでもこのライブラリの役割は、StringからNSAttributedStringを作るところまでに絞っています。
最後に
まだバグとかあるかもしれませんが、NSAttributedStringに対して自分と同じ苦しみを感じていらっしゃる方は是非使ってみてください。皆さんが気持ちよくプログラムを書いていく時の力になれれば幸いです。