こんにちは、関口@tanukiti1987 です。
今、おいしい健康では iOS アプリを開発しています。 そこで、ドロップキャップを使った表現が何箇所か登場する(予定)なのですが、 UITextView を純粋に使っただけでは、そういった表現が出来ずに、ちょっとした実装が必要になってきます。
今回は、そのちょっとした実装方法をご紹介していきます。
ドロップキャップ(drop-cap) とは
そもそも、ドロップキャップがなんなんだ。という話があると思います。 僕自身も実装を担当することになり、この言葉を知りました。
ドロップキャップは、こういうやつです。
1文字目が大きく表示され、以降1文字目を回り込むように文章が展開されていく例のやつです。
css の世界では
p:first-letter{ font-size: 2em; }
という指定をしてあげることで、このドロップキャップの表現が可能です。
いつもは嫌いだけど、この時ばかりは最高じゃないか、CSS
Swift ではどうやるのか
ここまで紹介しましたが、さきにも述べたように swift では :first-letter
みたいなベンリ attribute はないので、自前でがんばっていくことになります。
使っていくのは、 UITextView
UIImageView
の2つです。
- 一文字目を
UIImageView
としてコードで生成する TextKit
の exclusion path を指定することで 作ったUIImageView
の周りに文字を回り込ませる
この2点を行うことで、ドロップキャップを実現していきます。
文字を UIImageView として生成する
文字を画像として生成するには UIGraphics
を使って、描画していきます。
今回は、以下のような関数を作ってみました。
@IBOutlet weak var dropCapImageView: UIImageView! func createDropCapImage(text: String) -> UIImage { let size = CGSize(width: dropCapImageView.frame.size.width, height: dropCapImageView.frame.size.height) UIGraphicsBeginImageContext(size) let fontSize: UIFont = UIFont.systemFont(ofSize: 42.0) let style: NSMutableParagraphStyle = NSMutableParagraphStyle() style.alignment = NSTextAlignment.center style.lineBreakMode = NSLineBreakMode.byClipping style.minimumLineHeight = 30 let attributes = [ NSAttributedStringKey.font: fontSize, NSAttributedStringKey.paragraphStyle: style, NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.backgroundColor: UIColor.clear ] text.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height), withAttributes: attributes) let image: UIImage = UIGraphicsGetImageFromCurrentImageContext() as UIImage! UIGraphicsEndImageContext() return image }
font 情報を諸々設定したあとで text.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height), withAttributes: attributes)
で文字を書き、 UIGraphicsGetImageFromCurrentImageContext() as UIImage!
で画像を生成しています。
TextKit の exclusion path で文字を回り込ませる
先ほど作った画像を、UITextView の上に載せ、文字を回り込ませていきます。
こんな感じにviewを重ねます。
constraint などはよしなに調整してください。 今回のアプローチの少しイケてないところは、ドロップキャップの大きさを変えたり、テキストの attributes を変えると、レイアウトの微調整も必要になってくるところですね。。
ともあれ、他の方法もないので、この方法で突き進みます。
xibファイルが用意できたら、文字を回り込ませるような処理を書いていきます。
@IBOutlet weak var textView: UITextView! @IBOutlet weak var dropCapImageView: UIImageView! let text: String = "吾輩(わがはい)は猫である。名前はまだ無い。どこで生れたかとんと見当(けんとう)がつかぬ。" textView.text = String(text[text.index(text.startIndex, offsetBy: 1)...]) setTextAttributes() dropCapImageView.image = createDropCapImage(text: String(text[text.startIndex])) let exclusionRect = CGRect(x: 0, y: 0, width: dropCapImageView.frame.size.width, height: dropCapImageView.frame.size.height) let path: UIBezierPath = UIBezierPath(rect: exclusionRect) textView.textContainer.exclusionPaths = [path]
また、ここで忘れてはいけないのは、1文字目は既に画像としてViewに含ませているので、2文字目以降の文字を UITextView
に乗せていくというところです。
UITextView
に2文字目以降の文字を代入し、 UIImageView
に1文字目の画像を載せていきます。
最後に画像の周辺にテキストを回り込ませるための exclution path を生成し、 UITextView.textContainer.exclusionPaths
に登録します。
ここまでいけば、完成です!
このあたりのコードを実装していけば、冒頭ご紹介したような
このような表示ができるはずです。
おしまいに
今回ご紹介したコードはこちらにおいておきました。
Xcode9.1 で作っていますので、古くて動かなくなったらご愛嬌ということで。 是非、みなさまのご参考になればと思います。
おしまいのおしまいに
おいしい健康では、一緒に Swift で iOS アプリを作ってくれる仲間を募集しています! 新しいプロジェクトであり、会社のコアプロダクトを作っているところですので、自由度も高く、もりもり開発していけます。
何より、この記事の内容を見て、思うところがある方とはぜひお話してみたいところです ( ̄ー ̄)
Swift初心者の関口がお送りしました〜。