TextView
内で文字数制限をしたいことがあると思いますが、横画面対応してるアプリである条件の時にクラッシュしたため、気をつけることをまとめました。
環境
- Xcode10.1
- Swift4.2
文字制限コード
基本的な文字数制限のコードは以下のように書けます。
ViewController.swift
import UIKit
class ViewController: UIViewController {
enum Const {
static let maxStringCount = 5
}
@IBOutlet var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
}
extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
guard let text = textView.text else { return }
if textView.markedTextRange == nil && textView.text.count > Const.maxStringCount {
let endIndex = text.index(text.startIndex, offsetBy: Const.maxStringCount)
textView.text = String(text[..<endIndex])
}
}
}
注意点
しかしこれだと、iPhoneの横画面やiPadの場合、キーボード入力でUndo・Redoした際にクラッシュします。
Undoとは以下のスクリーンショットのオレンジ色の部分です。
Undoスクリーンショット
iPhone | iPad |
---|---|
対策
Undo・Redo中なのかどうかを判断するため、textDidChange
内でtextView.undoManger.isUndoing
とtextView.undoManger.isRedoing
を条件に追加します。
完成コード
ViewController.swift
import UIKit
class ViewController: UIViewController {
enum Const {
static let maxStringCount = 5
}
@IBOutlet var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.delegate = self
}
}
extension ViewController: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
guard let text = textView.text,
let isUndoing = textView.undoManager?.isUndoing,
let isRedoing = textView.undoManager?.isRedoing else { return }
if !isUndoing
&& !isRedoing
&& textView.markedTextRange == nil
&& textView.text.count > Const.maxStringCount {
let endIndex = text.index(text.startIndex, offsetBy: Const.maxStringCount)
textView.text = String(text[..<endIndex])
}
}
}
動作GIF
Undoしても問題なく動作します。
動作 | 動作GIF |
---|---|
文字数オーバー→確定 | |
文字数オーバー→Undo |