iOS 7 のテキスト入力欄(UITextView)の問題について
iOS 7 はメジャーバージョンアップということで未だ多くのバグがあるようです。中でも、複数行テキストの入力欄として用いられる UITextView という部品はかなりの問題を抱えたままです。
iOS 7 ではテキストの表示処理を司る新たなフレームワーク(TextKit)が導入され、システム全体で文字の大きさを変えたり、文字サイズに応じて自動的に太さが調整されるといったことが可能になっていますが、一方で、従来からあるUI部品への統合が十分でないまま、リリースされてしまったようです。
実際、Apple の開発者フォーラムでは、iOS 7 の UITextView に関するものだけで100以上のスレッドが立っており、様々な問題が投稿されています。
問題のほとんどは、iOS 7 のベータ版からあったものです。そのため開発者たちはかなり前からそれらに気づき、Apple へのバグ報告をしてきました。私もいくつかの報告をしました。しかし修正されないまま iOS 7 はリリースされました。
具体的には、主に次のような不具合があります。
- テキストのコンテナの高さが内部的に間違った値で扱われている模様。そのためスクロール量の計算が狂ったり、後ろの方の行が表示されなかったり、文字量に応じて動的にテキストビューの高さを変化させるといった処理がうまく作れない。この問題によってテキスト入力欄の基本的な描画処理の品質が大きく損なわれている。何をしても不安定な感じになる。
- タッチされた座標が内部的に間違った値で扱われている模様。そのため、カーソル位置がいきなり変なところに飛んだり、虫眼鏡を出した状態で空の行にカーソルを持って行けない。空の行でメニュー(コピー、ペースト…)が出ない。特に先頭部分の空行と末尾部分の空行。タッチ位置に応じた処理がうまく作れない。
- 任意の位置までスクロールさせる処理が正しく機能しない。そのため、ステータスバーをタップしても先頭までスクロールしてくれない。タイプ中にカーソル位置が常に見えるように自動スクロールしてくれない。特定箇所までスクロールさせるような処理がうまく作れない。
- データ検出をオンにして文中のURLなどをリンク化した場合に、オフにしてもそのスタイルが残る。あるいはプログラムから指定した文字のスタイルが正しく反映されない。
- (その他もろもろ)
また、iOS 7 のUIガイドラインでは、画面上部のナビゲーションバーや下部のキーボード部分を半透明にしてコンテンツはそれらの下に入り込むようにスクロールさせるべしとうたっているのですが、そういう作りのレイアウトを実装すると、上記の不具合が顕著に現れてしまうというのも困った点です。
これらは Xcode でただ UITextView を配置するだけで再現可能で、要は、そのままでは普通に文字入力することすらできない部品になってしまっています。
将来 iOS 7 のアップデータによって不具合は解消されると思いますが、それまでの暫定対処として、多くの開発者が問題を回避する方法を試行錯誤しています。しかしいわゆるプライベートメソッドを使った(公式に許可されていない内部プログラムへアクセスした)方法をとるとアプリが審査に通らないので、アプリ開発者ができることは限られています。これもなんだか困った話です。
ちなみに、上に書いたような不具合は、純正のメモアプリやメールアプリでは起こりません。これらは UITextView を使っていないのかもしれません。
なお、iOS 7 上であっても、iOS 6 コードで生成されたアプリでは上のような問題は起きません。内部的に、iOS 6 の部品が適用されるからです。ナビゲーションバーやキーボードが従来のグラデーションのデザインであれば、そのアプリは iOS 6 コードです。平坦な感じであれば iOS 7 コードです。
<追記 2013-10-23 20:55>
この記事を書いたすぐ後に iOS 7.0.3 が公開されました。
アップデートして試してみました。
空行にカーソルを合わせられない問題、先頭や末尾の空行でメニューを出せない問題は概ね修正されたようです。
ただし、テキストが途中までしか表示されない問題、入力中にカーソル位置まで自動スクロールしてくれない問題、任意の位置までプログラムから正しくスクロールさせられない問題、データ検出がおかしい問題などは依然残っているようです。また細かい部分で前には無かったおかしな挙動が新たに発生したりしていて、一進一退の状態。
なお、Textwell では上に書かれているような不具合をだいぶ解消しているつもりです(残念ながら完全ではありません)。そのために多くの暫定対処コードを書いています。もし今回の iOS アップデートで UITextView がまともになっていたら、その暫定コードを消すことができたのですが、ちょっとまだ消せないようです。
<追記 2013-11-15 12:28>
iOS 7.0.4 が出たので喜び勇んでインストール。UITextView の挙動を試してみましたところ…
結論:なーんも変わってね。
この UITextView の壊れっぷりをなんとかしようと色々試していて感じたのは、まず公開 API だけではもうどうにも対処できないということですけど、その前に、挙動の不可解さが半端じゃないことから、これはなんか Word に似てるなと。
プログラマー観点で、普通のバグとして考えたらあり得ないような異常動作をするわけです。バグというのは大抵、例外処理が不十分な場合に顕在化するわけですけど、もう例外じゃなくて主要な振る舞いがぜんぜんできていないわけですから。
だから iOS 7 で UIKit にTextKit を組み込んだ時の基本的な仕組みがもう失敗していて、もしかしたら結構深いところで作り直しが必要になっているのではないかと。
そうだとすると、バージョンとしては 7.1 が出るまで放置かもしれません。
<追記 2014-03-11 16:10>
待望の iOS 7.1 がリリースされました。
ベータの期間が長かったので、その出来映えについてはずっと見て来ていましたが、NDA のためにここには書けず。
で、結論は。
… ほぼなんも変わってないっすね。
まともになってないです。なんとなーくよくなったような気がしないでもないですけどたぶんプラシーボでしょう。
デベロッパーのフォーラムにも、「バグレポートしたのに Apple の人は全く対応してないよ」というタレコミがいくつかあります(対応されるとそれが分かる仕組みになっています)。
文章の末尾に文字を打っていっても自動でスクロールしてくれない現象などは、もはや仕様なんじゃないかという気もして来ました。(GUI の常識ではキャレットが常に見えるように自動スクロールする)
新機能を盛り込むよりも前に、まずは基本的なUI部品をまともにしてもらいたいものですが、この調子でいくと、iOS 8 まで放置かもしれません。
ところで余談ですがこちらの記事には驚きました。
アプリが起動する時に「旧 UITextView を使え」という呪文を唱えると、iOS 6 までの部品が使われるようになって、問題がすべて解決するというのです。実際にやってみたら本当でした。すごい。
この方法、いったいどうやって発見したのでしょうか。しかも、こんな重要なフラグが、standardUserDefaults という、開発者が簡単に読み書きできるところにあるというのも謎です。
ただしこの方法は Apple から正式に公開されているものではないため、ストアに出すアプリに用いるのはちょっと憚られます。
OS のバージョンアップで仕様が変わって重大なバグになる恐れもありますし、iOS 7 の新機能(ダイナミックタイプとか)ももちろん使えなくなります。
というわけで、この方法はものすごく魅力的なんですけど、総合的に考えるとストアに出すアプリには使えないですね。それにしてもすごい発見だと思います。
<追記 2015-08-25>
その後 iOS 8 が出て、ほんの少しだけ UITextView なおってましたけど、やっぱりかなりおかしくて。
で、2015年、iOS 9 のベータが出たので試したところ、なんと、というかいまさらというか、ふう、どうやらやっとまともな感じになっているようです。
あれだけおかしなものを2年も放置していた Apple ほんと信じられないです。あきれて、もうこの UITextView のことは話題にもしたくない。
<追記 2015-11-30>
iOS 9 では UITextView がやっと直ったと書きましたが…、その後の動作検証の結果、やっぱりおかしい、直ってない。でした。
ということで、iOS 9 をターゲットにした Textwell v1.5 では、引き続きバグ回避のための補正処理を含めたままとしています。