UITextViewとNSRangeを組み合わせたテキスト操作入門 ✍️📄📱
UITextViewとNSRangeを組み合わせることで、テキストの選択や検索、装飾、スクロール制御など、さまざまな機能を実装できます。本記事では、UIKitのUITextViewを使った基本的なNSRangeの仕組みから応用例まで解説します。🔍✨🎨
はじめに 🎯🔰💡
- 対象読者:UITextViewを利用したテキスト操作を学びたい人
- 目的:NSRangeの基本を理解し、UITextViewでの選択制御やハイライト、スクロール制御などを実装できるようになる
📚 参考資料:
1. NSRangeの基本 📐🔢🔍
NSRange
は、配列や文字列の一部分を指定するために使う構造体で、location
(開始位置)とlength
(要素数)の2つの値を持ちます。たとえば文字列の5文字目から10文字分を扱いたいときに使います。📝📏📚
// C言語定義
struct _NSRange {
NSUInteger location;
NSUInteger length;
};
typedef struct _NSRange NSRange;
-
NSNotFound
:検索結果が見つからなかったときの値 -
NSMakeRange(loc, len)
:開始位置loc
と長さlen
で範囲を作成 -
NSMaxRange(range)
:location + length
で範囲の終わりを取得 -
NSLocationInRange(pos, range)
:位置pos
がrange
の中にあるか判定 -
NSStringFromRange
/NSRangeFromString
:範囲を文字列に変換/文字列から範囲に変換
関連クラス 💼🏷️📦
以下のクラスでNSRangeが使われる場面をまとめました。
クラス名 | 用途 |
---|---|
NSString | 不変文字列の部分抽出・検索 |
NSMutableString | 文字列の挿入・削除時の範囲指定 |
NSArray | 不変配列の部分抽出 |
NSMutableArray | 配列の要素追加・削除の範囲操作 |
NSAttributedString | 装飾文字列の属性適用範囲 |
NSMutableAttributedString | 属性の追加・変更範囲 |
NSTextStorage | テキストと属性の管理、編集範囲の通知 |
NSLayoutManager | レイアウトや描画時のグリフ範囲 |
NSTextContainer | テキストの描画領域設定 |
2. UITextViewとNSRangeの組み合わせ 📲✏️🔧
2.1 テキスト選択の制御 🔲✅👆
アプリから任意の範囲を選択状態にできます。以下は開始位置5、長さ10の範囲を選択する例です。
let textView = UITextView()
let startIndex = 5
let selectionLength = 10
textView.selectedRange = NSMakeRange(startIndex, selectionLength)
-
selectedRange
を設定するだけで選択できる -
textViewDidChangeSelection(_:)
で選択範囲変更を検知可能
📚 UITextViewDelegate.textViewDidChangeSelection(_:) — Apple Developer
2.2 検索ワードのハイライト 🔎✨🖍
正規表現で見つけた範囲を背景色でハイライトする例です。
let text = textView.text ?? ""
let attributed = NSMutableAttributedString(string: text)
let regex = try! NSRegularExpression(pattern: "重要キーワード")
let matches = regex.matches(
in: text,
range: NSMakeRange(0, text.utf16.count)
)
matches.forEach { match in
attributed.addAttribute(
.backgroundColor,
value: UIColor.yellow,
range: match.range
)
}
textView.attributedText = attributed
📚 NSRegularExpression — Apple Developer
2.3 スクロールによるフォーカス移動 📜⬇️📍
指定した位置まで自動でスクロールします。
textView.scrollRangeToVisible(NSMakeRange(50, 0))
- 長い文章の中で特定箇所をユーザーに見せたいときに便利
📚 UITextView.scrollRangeToVisible(_:) — Apple Developer
3. UIFontTextViewの内部構造とNSRange応用 🏗️🔍🔄
UITextViewは以下のコンポーネントでテキスト編集機能を実現しています。
- NSTextStorage
- NSLayoutManager
- NSTextContainer
3.1 NSTextStorage 📦📝⚙️
テキストと属性を管理し、変更時にNSRangeで編集範囲を通知します。
class HighlightTextStorage: NSTextStorage {
private let storage = NSMutableAttributedString()
override var string: String { storage.string }
override func attributes(at location: Int, effectiveRange range: NSRangePointer?) -> [NSAttributedString.Key : Any] {
storage.attributes(at: location, effectiveRange: range)
}
override func replaceCharacters(in range: NSRange, with str: String) {
beginEditing()
storage.replaceCharacters(in: range, with: str)
edited([.editedCharacters, .editedAttributes], range: range, changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(_ attrs: [NSAttributedString.Key : Any], range: NSRange) {
beginEditing()
storage.setAttributes(attrs, range: range)
edited(.editedAttributes, range: range, changeInLength: 0)
endEditing()
}
}
📚 NSTextStorage — Apple Developer
3.2 NSLayoutManager 🖼️🔲📐
グリフ単位のレイアウト情報をNSRangeで扱い、カスタム描画に活用できます。
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: textView.bounds.size)
layoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(layoutManager)
let glyphRange = NSMakeRange(10, 5)
layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { rect, _, _, _, _ in
// カスタム描画
}
📚 NSLayoutManager — Apple Developer
3.3 NSTextContainer 📐🗺️🔧
描画領域のサイズや余白を設定し、テキストの折り返しを制御します。
let textContainer = NSTextContainer()
textContainer.lineFragmentPadding = 5
textContainer.size = CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)
📚 NSTextContainer — Apple Developer
4. まとめ 🏁📌🎉
- NSRangeとUITextViewを組み合わせることで、選択操作、ハイライト、スクロール制御など多彩なテキスト機能を実装できる
- NSTextStorage/NSLayoutManager/NSTextContainerの連携を理解すると、さらに高度なカスタマイズが可能になる
- まずは基本のAPIを試し、次に内部コンポーネントを活用してリッチなテキスト機能を作り込もう🚀
以上の内容を参考に、自分のアプリや学習プロジェクトで実践してみてください!
📅 参考文献
Discussion