iOSDC 2018 でMarkdownの解析について発表してきた。
( ios )発表したぞ!
iOSDC 2018にコアスタッフ兼スピーカーとして参加させていただきました。 発表時間帯は初日でしかも朝一で発表しなければいけなく、人がくるのかと 心配していましたが、朝一にもかかわらずまぁまぁ人がきてくれたので少し安心しました。
発表のタイトルはこちら
Markdownをリアルタイムに解析する
https://fortee.jp/iosdc-japan-2018/proposal/a2e20820-d4f6-43e5-b34b-1b9e6fec7806
Markdownを解析する時にGitHubで探せば簡単にライブラリを何個も見つけることができます。しかし、をそれをリアルタイムとなるとなかなかみつけることができません。私は端末間で同期できるメモアプリを作成し、機能の一つに入力しながらMarkdown形式に色付けをする機能を実装しました。本トークではリアルタイムに文章を解析し、リッチな表現をどのようにして行っているのかを解説します。
資料
ざっくり解説
リアルタイムに処理をするために
以下の項目を基点に話を掘り下げていきました。
- カラーリング
- 入力補佐
カラーリング
一番リアルタイム性を考えなければいけないのが文字を入力した時です。
最小限の範囲で文字列操作を行う
いつ?
まず、、NSTextStorageDelegate
のメソッド(以下のメソッド)を使用して取得する必要がありました。
これは処理前にこのメソッドがコールされ、その中でで編集した領域を取得するができます。
さらにeditedRange
は複数行の場合があることを考慮に入れる必要があります。
// MARK: - NSTextStorageDelegate
extension TextViewController: NSTextStorageDelegate {
func textStorage(_ textStorage: NSTextStorage, willProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) {
self.editedRange = editedRange
}
}
何を?
それは変換するべき領域
です。以前のコードでeditedRange
を利用していますが、これは編集した領域であって変換する領域ではありません。以下のように、編集した時にそれに関わる行の最初の位置から最後の位置まで取得する必要があります。
この赤線の部分が一行ずつ抽出して属性を変更していきます。
どのようにして?
変換するべき領域
を抽出するんですがその時には以下の関数を使うと行の全ての範囲を取得する
ことが可能になります。
ここでは取得した行の範囲にあるlocationがわかっていれば期待した位置が取得できます。
- lineRange
絵文字
ここでは絵文字を扱う時に気をつけようと話をしています。そもそもユニコードの話から文字コード仕組みを話し、Swift.stringでは各文字コードでカウントすると変わってくることを示しました。それはどういうことなのかなぜそれを意識する必要があるのかという解説をしています。
また、文字列操作をするときにはNSString、NSRangeを使うことになりますがこの仕様を理解して文字列の操作を行って行くことが重要になってきます。
型の内部仕様
なので範囲を指定する時にはUTF16を意識した文字列操作が必要になってきます。
NSRange(location: 0, length: line.utf16.count)
入力補佐
タブやバックタブや改行による自動インデント
とリスト表現を改行しても同じ構造にしておく必要があります。ちなみにIzumoではオプションで自動でするかしないかを選択することができます。
- オートインデント
- 改行してもリスト構造を復元する
- タブでの移動
- リストの構造を崩さず深くしたりする
つまり、以下のイベント時に文字列の操作を必要がありました。
- タブ
- バックタブ
- 改行
さらに、アプリを作成するときに気をつけなければいけなかったのがiOSだけでなくmacOSを考慮する必要があります。
- NSTextView
- UITextView
この二つの仕様を意識して実装する必要があります。 ここではNSTextViewにテキスト操作に必要なイベント用意されていたのですが、UITextViewには僕たちが期待したメソッドが用意されていなかったので自分でそれに取って代わる実装をする必要があったという話をしました。(モバイルはそこまでキーボードで入力するということ期待していない設計になっているので用意されていないのはわかるけど面倒だった)
通常のString.insert
でやろうとするとカーソルやエベント期待通りに動いてくれいないことがありました。
なのでinsertText
を利用すれば問題は解決するのではと思い使って見ました。がそれでも期待通りに動いてくれませんでした。
期待通りになるまで実装を全て自分で行う必要ありました。
まとめ
- 解析はるべく最小限の範囲で行う
- 文字コードを意識した文字列操作
- NSString, NSRange
- 入力補佐時のiOSの設計
- カスタマイズが必須
所感
去年に引き続きそれなりにニッチな話となったが、自分の過去一年のまとめになりましたし、発表したところ複数の人からも興味を持ってくれた人がよかったので話したかいがありました。よかった。よかった。 また、僕以外の素晴らしい発表をまだ見切れていないので自分のペースでゆっくり見ていこうと思う。