Swift 4 UITextViewを便利に使うExtensionを書いてみた


はじめに

こんにちは
UITextViewを細かく制御しようと思った時、
調べた時に記事が少し古かったり、Swift4.0で名前が変わっていたりで少し悩んだので、まとめとして書いてみました。参考になれば幸いです。
Apple Swift version 4.1 Xcode Version 9.3.1

サンプル

キャレットの位置を取得する

  • currentCaretOffset

左から数えて、見た目上の何番目にキャレットがあるか返します。

// デフォルトにあるプロパティ
textView.selectedRange.location // 8
// Extension
textView.currentCaretOffset     // 7

キャレットを先頭・末尾に移動させる

  • caretMove(to: UITextViewDirection)

// ←ボタン 先頭へ移動します
textView.caretMove(to: .leading)
// →ボタン 末尾に移動します
textView.caretMove(to: .trailing)

キャレットを現在の位置から移動させる

  • caretMove(to: Int)

現在の位置から指定の数を移動させます。
+ならば右方向へ、-なら左方向へ移動します。

// →ボタン
textView.caretMove(to: 1)
// ←ボタン
textView.caretMove(to: -1)

キャレットの色を変更する

  • caretColor

実は好みで変数名を変えただけです。

// キャレットを緑色に変更する
textView.caretColor = UIColor.green

文字を全て選択する

  • selectAllTexts()

UITextView内の文字を全て選択します。

// 文字全てを選択します
textView.selectAllTexts()

文字を指定範囲にて選択する

  • selectTexts(start:length:)

startは開始位置、lengthは選択する範囲です。

// 見た目上の文字の長さから選択します
textView.selectTexts(start: 0, length: 7)

文字が選択されているかどうか

  • isTextSelected

UITextRangeisEmptyは以下の画像のようになっていて、それを少しだけ改良してみました。

ソースコード

変数名などご自由に変更してください。

UITextFieldExt.swift
import UIKit

extension UITextView {

    enum UITextViewDirection {
        case leading
        case trailing
    }

    // キャレットの色を変更します
    var caretColor: UIColor {
        get { return self.tintColor }
        set { self.tintColor = newValue}
    }

    // 文字が選択されているかどうか
    var isTextSelected: Bool {
        guard let range = selectedTextRange else { return false }
        return !range.isEmpty
    }

    // 先頭から数えたキャレットの見た目上の文字の位置を示します
    var currentCaretOffset: Int {
        let current = selectedRange.location
        let diff = text.length - text.count
        return current - diff
    }

    // キャレットを移動させます
    func caretMove(to direction: UITextViewDirection) {
        switch direction {
        case .leading:
            self.selectedTextRange = textRange(from: beginningOfDocument, to: beginningOfDocument)
        case .trailing:
            self.selectedTextRange = textRange(from: endOfDocument, to: endOfDocument)
        }
    }

    // キャレットを移動させます
    func caretMove(to offset: Int) {
        let current = selectedRange.location
        let to = current + offset
        self.selectedRange = NSRange(location: to, length: 0)
    }

    // 文字全てを選択します
    func selectAllTexts() {
        self.selectedTextRange = textRange(from: beginningOfDocument, to: endOfDocument)
    }

    // 見た目上の文字の長さから選択します
    func selectTexts(start: Int = 0, length: Int) {
        let diff = text.length - text.count
        self.selectedRange = NSRange(location: start, length: length + diff)
    }

    // NSRangeとして取得します
    var selectedTextNSRange: NSRange? {
        guard let range = selectedTextRange else { return nil }
        let location = offset(from: beginningOfDocument, to: range.start)
        let length = offset(from: range.start, to: range.end)
        return NSRange(location: location, length: length)
    }
}

参考にさせていただいた記事

見て頂いてありがとうございます。