Неожиданное поведение автомакета, когда для параметра isScrollEnabled TextView задано значение false

Я пытаюсь увеличить текстовое представление и сделать его прокручиваемым при некотором размере содержимого. Что я делаю:

func textViewDidChange(_ textView: UITextView) {
        if textView.contentSize.height > 100 && !textView.isScrollEnabled {
            let frame = textView.frame
            textView.isScrollEnabled = true
            let heightConstraint: NSLayoutConstraint = .init(item: textView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: frame.height)
            heightConstraint.identifier = "height"
            textView.addConstraint(heightConstraint)
        }
        
        if textView.contentSize.height < 100 && textView.isScrollEnabled {
            textView.isScrollEnabled = false
            let heightConstraint = textView.constraints.first { constraint in
                constraint.identifier == "height"
            }
            textView.removeConstraint(heightConstraint!)
        }
        print(textView.contentSize.height, textView.frame.height)
    }

если размер содержимого больше 100, я включаю прокрутку и добавляю ограничение по высоте

если размер содержимого меньше 100, я отключаю прокрутку и удаляю ограничение (я ожидаю, что текстовое представление будет соответствовать его содержимому, как это обычно происходит, когда прокрутка отключена)

у меня нет проблем с включением прокрутки, но когда размер содержимого становится меньше 100, срабатывает второй оператор if, и текстовое представление по какой-то причине занимает все пространство экрана. когда я снова вызываю textViewDidChange (путем удаления или добавления чего-либо в текстовое представление), оба ifs срабатывают, и все работает так, как должно быть.

то, что я пытался сделать, это вызвать textview.sizeToFit() и view.layoutIfNeeded(), но безуспешно. что я здесь делаю неправильно?


person belotserkovtsev    schedule 23.03.2021    source источник


Ответы (1)


Я думаю, ты делаешь гораздо больше, чем нужно.

Лучшим подходом было бы создать ограничение высоты, равное постоянному значению 100, а затем активировать/деактивировать его по мере необходимости.

Попробуйте это так:

class ViewController: UIViewController, UITextViewDelegate {
    
    let myTextView = UITextView()
    
    var tvHeightConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        myTextView.translatesAutoresizingMaskIntoConstraints = false
        myTextView.font = .systemFont(ofSize: 18.0)
        myTextView.backgroundColor = .yellow
        
        view.addSubview(myTextView)
        
        let g = view.safeAreaLayoutGuide
        
        tvHeightConstraint = myTextView.heightAnchor.constraint(equalToConstant: 100.0)
        
        NSLayoutConstraint.activate([
            myTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            myTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            myTextView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
        ])
        
        myTextView.isScrollEnabled = false
        
        myTextView.delegate = self
    }

    func textViewDidChange(_ textView: UITextView) {
        textView.isScrollEnabled = (textView.contentSize.height > 100)
        tvHeightConstraint.isActive = textView.isScrollEnabled
    }
    
}
person DonMag    schedule 24.03.2021
comment
такая же проблема. текстовое представление занимает все доступное пространство, когда ограничение удаляется, что заставляет метод делегата включать ограничение при его следующем вызове. поэтому вид продолжает расширяться и сжиматься снова и снова - person belotserkovtsev; 24.03.2021
comment
другая проблема заключается в том, что размер текстового представления может быть больше 100, что приводит к его уменьшению при включении прокрутки. вот почему я создаю ограничение и запускаю его с текущей высотой кадра - person belotserkovtsev; 24.03.2021
comment
Вы используете точный код, который я опубликовал? Все само собой? Или вы используете этот код и пытаетесь реализовать его с остальной частью вашего макета? Я не вижу проблемы, которую вы описываете. - person DonMag; 24.03.2021
comment
У меня другой макет, поэтому я должен был принять его. я создал ограничение по высоте в раскадровке, подключил его через розетку, установил myTextView.translatesAutoresizingMaskIntoConstraints = false в viewDidLoad и заменил textViewDidChange() на вашу реализацию - person belotserkovtsev; 24.03.2021
comment
@belotserkovtsev - ну, мне нужно посмотреть, что еще вы делаете со своим макетом, и посмотреть, какие другие ограничения у вас есть, которые могут повлиять на него. - person DonMag; 24.03.2021
comment
мое текстовое представление встроено в представление-оболочку, которое встроено в другое представление. ни один из них не имеет ограничений по ширине или высоте. только ограничения по площади. я действительно не вижу причины, чтобы он не работал - person belotserkovtsev; 24.03.2021
comment
@belotserkovtsev - не уверен, что вы имеете в виду под отсутствием ограничений по ширине или высоте. Если вы указали, например, 20-pt начального и конечного пробела, это определяет ширину. Не видя вашего фактического макета, чтобы понять, что происходит не так, я не могу больше ничего сказать, чтобы помочь. Возможно, взгляните на этот вопрос и ответ: представление стека с включенной прокруткой">stackoverflow.com/questions/46965439/ ... возможно, похоже на то, что вы делаете, и может помочь. - person DonMag; 24.03.2021