web-dev-qa-db-ja.com

NSAttributedStringの背景色と丸い角

カスタムUIViewの角の丸みとテキストの背景色に関する質問があります。

基本的に、カスタムUIViewで次のような効果を実現する必要があります(添付画像-片側の丸い角に注意してください): Background highlight

私は使用するアプローチは次のように考えています:

  • コアテキストを使用して、グリフの実行を取得します。
  • ハイライト範囲を確認してください。
  • 現在の実行がハイライト範囲内にある場合は、グリフの実行を描画する前に、丸い角と目的の塗りつぶし色で背景の四角形を描画します。
  • グリフランを描画します。

ただし、これが唯一の解決策であるかどうか(または、これが最も効率的な解決策であるかどうか)はわかりません。

UIWebViewの使用はオプションではないため、カスタムUIViewで行う必要があります。

私の質問は、これが使用するための最良のアプローチであり、私は正しい軌道に乗っていますか?それとも、重要な何かを見逃したり、間違った方法で進んでいますか?

48
codeBearer

私は上記の効果を達成することができたので、同じことに対する答えを投稿すると思いました。

これをより効果的にするための提案があれば、遠慮なく投稿してください。あなたの答えを正しいものとしてマークします。 :)

これを行うには、NSAttributedStringに「カスタム属性」を追加する必要があります。

基本的に、それはNSDictionaryインスタンスに追加できるものであれば、任意のキーと値のペアを追加できるということです。システムがその属性を認識しない場合、何もしません。その属性のカスタム実装と動作を提供するのは、開発者としてのあなた次第です。

この回答の目的のために、_@"MyRoundedBackgroundColor"_という値を持つ_[UIColor greenColor]_というカスタム属性を追加したと仮定します。

以下の手順では、CoreTextがどのように処理を行うかについての基本的な理解が必要です。 Appleのコアテキストプログラミングガイド フレーム/ライン/グリフラン/グリフなどを理解するには.

そのため、手順は次のとおりです。

  1. カスタムUIViewサブクラスを作成します。
  2. NSAttributedStringを受け入れるためのプロパティがあります。
  3. CTFramesetterインスタンスを使用してNSAttributedStringを作成します。
  4. _drawRect:_メソッドをオーバーライドします
  5. CTFrame。からCTFramesetterインスタンスを作成します
    1. CGPathRefを作成するには、CTFrameを指定する必要があります。 CGPathが、テキストを描画するフレームと同じになるようにします。
  6. 現在のグラフィックスコンテキストを取得し、テキスト座標系を反転します。
  7. CTFrameGetLines(...)を使用して、作成したCTFrameのすべての行を取得します。
  8. CTFrameGetLineOrigins(...)を使用して、CTFrameのすべての行の原点を取得します。
  9. _for loop_を開始します-CTLine...の配列の各行に対して.
  10. CGContextSetTextPosition(...)を使用して、テキスト位置をCTLineの先頭に設定します。
  11. CTLineGetGlyphRuns(...)を使用すると、CTRunRefからすべてのグリフ実行(CTLine)を取得できます。
  12. 別の_for loop_を開始します-CTRun...の配列内の各glyphRunに対して.
  13. CTRunGetStringRange(...)を使用して実行範囲を取得します。
  14. CTRunGetTypographicBounds(...)を使用して活版印刷の境界を取得します。
  15. CTLineGetOffsetForStringIndex(...)を使用して、実行のxオフセットを取得します。
  16. 前述の関数から返された値を使用して、バウンディング長方形を計算します(runBoundsと呼びましょう)。
    1. 要確認-CTRunGetTypographicBounds(...)は、テキストの「上昇」と「下降」を保存するための変数へのポインターを必要とします。ランの高さを取得するにはこれらを追加する必要があります。
  17. CTRunGetAttributes(...)を使用して実行の属性を取得します。
  18. 属性ディクショナリに属性が含まれているかどうかを確認してください。
  19. 属性が存在する場合、ペイントする必要がある長方形の境界を計算します。
  20. コアテキストは、ベースラインに行の起点があります。テキストの最下部から最上部まで描画する必要があります。したがって、降下に合わせて調整する必要があります。
  21. したがって、ステップ16で計算した境界四角形から下降を減算します(runBounds)。
  22. runBoundsができたので、ペイントしたい領域がわかりました-CoreGraphis/UIBezierPathメソッドのいずれかを使用して、特定の四角形を描画して塗りつぶすことができます丸い角。
    1. UIBezierPathには_bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:_と呼ばれる便利なクラスメソッドがあり、特定のコーナーを丸めることができます。 2番目のパラメーターでビットマスクを使用してコーナーを指定します。
  23. 四角形を埋めたので、CTRunDraw(...)を使用してグリフランを描画するだけです。
  24. カスタム属性を作成したことで勝利を祝います-ビールなどを飲みます! :D

属性範囲が複数の実行にまたがることを検出することに関して、最初の実行で属性に遭遇したときにカスタム属性の有効範囲全体を取得できます。属性の最大有効範囲の長さが実行の長さよりも長い場合、右側に鋭い角をペイントする必要があります(左から右のスクリプトの場合)。さらに数学を使用すると、次の行のハイライトコーナースタイルも検出できます。 :)

効果のスクリーンショットが添付されています。上部のボックスは標準のUITextViewであり、そのためにattributedTextを設定しました。下部のボックスは、上記の手順を使用して実装されたものです。同じ属性文字列が両方のtextViewに設定されています。 custom attribute with rounded corners

繰り返しますが、私が使用したものよりも良いアプローチがあれば、教えてください! :D

これがコミュニティに役立つことを願っています。 :)

乾杯!

55
codeBearer