Я потратил много времени, пытаясь исправить эту простую проблему, но не справился. Я хочу, чтобы в UITextView использовался заполнитель: «перевод», и при нажатии на UITextView заполнитель должен оставаться там до тех пор, пока не будет введен символ, но мне нужно, чтобы курсор был помещен в начало заполнителя непосредственно перед словом «перевод», но он остается в конце моего заполнителя. Я нашел несколько ответов и попытался сделать это следующим образом, но не работает. Заполнитель правильно меняется на черный цвет, но вторая команда -> textView.selectedRange = NSMakeRange(0, 0) не перемещает курсор в начало, почему?
Вот полный код:
class MainViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {
@IBOutlet weak var definitionField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// setting placeholder
definitionField.textColor = .gray
definitionField.text = "translation"
definitionField.delegate = self }
func textViewDidBeginEditing(_ textView: UITextView) {
if (textView.text == "translation") {
textView.textColor = .black
textView.selectedRange = NSMakeRange(0, 0)
}
}
func textViewDidEndEditing(_ textView: UITextView) {
if (textView.text == "") {
textView.text = "translation"
textView.textColor = .gray
}
textView.resignFirstResponder()
}
Похоже, что selectedTextRange
автоматически устанавливается системой после textViewDidBeginEditing
и вызывается becomeFirstResponder
. Вы можете наблюдать это, используя собственный подкласс UITextView
и переопределяя:
override var selectedTextRange: UITextRange? {
didSet {
print(selectedTextRange)
}
}
Вы увидите, что это сначала устанавливается на (0, 0)
из-за вашего кода, а затем didSet
снова "волшебным образом" вызывается, устанавливая selectedTextRange
на (11, 0)
.
Я не уверен, какой метод сделал вызов. После этого может даже не быть переопределяемого метода.
Простым решением было бы просто использовать DispatchQueue.main.async
, чтобы дождаться того, что установит выделенный текстовый диапазон в конец, а затем изменить выделение.
// in textViewDidBeginEditing
DispatchQueue.main.async {
textView.selectedRange = NSMakeRange(0, 0)
}
Курсор появится в конце на короткое время, прежде чем перейти к началу. Кажется, это только визуально. Когда я пытался ввести текст, когда курсор находится в конце, текст вставляется перед «переводом».
Тем не менее, ваша реализация этого заполнителя, похоже, сильно отличается от того, как можно было бы ожидать, что заполнитель будет работать (например, в UITextField
), особенно когда textViewDidEndEditing
.
Если вам нужно поведение, похожее на UITextField
, или если вы хотите увидеть другие подходы, ознакомьтесь с этим постом.
Спасибо, эффективно с DispatchQueue он работает с этим очень коротким временем, когда курсор появляется в конце. Сообщение, которым вы поделились, работает лучше, но единственный недостаток заключается в том, что после прикосновения к textView заполнитель мгновенно исчезает.
Самое простое решение здесь — добавить метку, содержащую заполнитель. Затем установите его свойство isHidden
в зависимости от того, есть ли текст в текстовом представлении.
Вот рабочий пример но он использует RxSwift для отслеживания изменений текста, цвета и шрифта.
Что оно делает:
extension UITextView {
func withPlaceholder(_ placeholder: String) {
let label = {
let result = UILabel(frame: bounds)
result.text = placeholder
result.numberOfLines = 0
result.frame = result.frame.offsetBy(dx: 4, dy: 8)
return result
}()
addSubview(label)
_ = rx.observe(UIColor.self, "tintColor")
.take(until: rx.deallocating)
.bind(to: label.rx.textColor)
_ = rx.observe(UIFont.self, "font")
.map { $0 != nil ? $0 : UIFont.systemFont(ofSize: 12) }
.take(until: rx.deallocating)
.bind(onNext: { [weak self] font in
label.font = font
label.frame.size = label.sizeThatFits(self?.bounds.size ?? CGSize.zero)
})
_ = rx.text.orEmpty.map { !$0.isEmpty }
.take(until: rx.deallocating)
.bind(to: label.rx.isHidden)
}
}
Первоначально я ответил на это, предлагая использовать
placeholder
, потому что я перепуталUITextField
сUITextView
. У первого есть заполнитель, но OP использует последний. я удалил свой ответ