Прокрутите вниз до конца текстового меню

Я сделал кнопку, которая добавляет некоторый текст в textView, и я хочу, чтобы она автоматически прокручивалась вниз, когда она была нажата, чтобы пользователь мог видеть добавленный новый текст.
Я не могу использовать это решение в Swift, потому что не знаю Objective-C. Кто-нибудь знает, как я могу прокручивать нижнюю часть текста в Swift? Благодарю.

Ответ 1

Пробовал решения по смещению контента и scrolltoview, получал смешанные результаты и прерывистую прокрутку; оглядевшись вокруг ниже, казалось, работал, и при необходимости последовательная прокрутка вниз.

В viewdidload:

self.storyTextView.layoutManager.allowsNonContiguousLayout = false

Затем, когда это необходимо:

let stringLength:Int = self.storyTextView.text.characters.count
self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))

Ответ 2

Swift 4

let bottom = NSMakeRange(textLog.text.count - 1, 1)
textLog.scrollRangeToVisible(bottom)

Swift 3

let bottom = NSMakeRange(textLog.text.characters.count - 1, 1)
textLog.scrollRangeToVisible(bottom)

Обновление: спасибо @AntoineRucquoy для Swift 4 напоминания!

Ответ 3

Поэтому, если вы нажмете ссылку, которую вы опубликовали, принятый ответ показывает эту цель: C-код:

-(void)scrollTextViewToBottom:(UITextView *)textView 
{
  if(textView.text.length > 0 ) 
  {
    NSRange bottom = NSMakeRange(textView.text.length -1, 1);
    [textView scrollRangeToVisible:bottom];
  }
}

Поэтому ваша задача - преобразовать этот код в Swift.

Разбейте его на куски и займите их по одному. Во-первых, определение самого метода.

Этот метод называется scrollTextViewToBottom. В качестве параметра он принимает UITextView и не возвращает результат. Как бы вы могли написать это определение метода в Swift?

Далее посмотрите, что тело метода. Оператор if должен быть точно таким же в Swift. Создание NSRange практически идентично. Вам просто нужно немного изменить его:

let bottom = NSMakeRange(textView.text.length -1, 1)

Часть, которая, вероятно, самая трудная для тех, кто не знает Objective-C, - это вызов метода. Он отправляет сообщение scrollRangeToVisible в объект textView. Прошедший параметр - bottom. Посмотрите, можете ли вы переписать эту строку в Swift. Затем соедините все это.

Ответ 4

Просто, где myTextView - это UITextView о UITextView идет речь:

let bottom = myTextView.contentSize.height

myTextView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true) // Scrolls to end

Ответ 5

Язык: Swift

Выполните следующие шаги:
// Объявляем

@IBOutlet weak var trTextDataRead: UITextView!  

//Метод Кунстома

func insertTextView(text: String){
 //Insert text
 trTextDataRead.text.append(text)  

 //Scroll to the end  
 let btm = NSMakeRange(trTextDataRead.text.lengthOfBytes(using: String.Encoding.utf8), 0)  
 trTextDataRead.scrollRangeToVisible(btm)
}

Ответ 6

UITextView имеет свойство contentOffsent. Вы можете либо установить textView.contentOffset либо textView.setContentOffset(offset, animated: true)

Например, если contentSize вашего текстового вида (100, 500) но высота текстового представления составляет всего 100, затем для прокрутки до нижней части, установите для свойства contentOffset значение (0, 400) (это для вертикального текста Посмотреть). В более общем виде формулой для прокрутки вниз является textView.contentSize.height-textView.height. Каждый раз, когда вы нажимаете кнопку, установите смещение.

Я бы также рекомендовал прочитать документацию и попытаться понять ее. Swift и iOS достаточно хорошо документированы, и такой вопрос легко найти с помощью Google.

Изменение: Это работает, потому что UITextView наследуется от UIScrollView.

Sidenote: Я написал подкласс UITextView где вы можете установить вертикальное выравнивание текста, поэтому, если вы установите выравнивание текста на .Bottom, текст будет выровнен с нижней частью представления.

class TextView: UITextView {

    enum VerticalAlignment: Int {
        case Top = 0, Middle, Bottom
    }

    var verticalAlignment: VerticalAlignment = .Middle

    //override contentSize property and observe using didSet
    override var contentSize: CGSize {
        didSet {
            let textView = self
            let height = textView.bounds.size.height
            let contentHeight:CGFloat = contentSize.height
            var topCorrect: CGFloat = 0.0
            switch(self.verticalAlignment){
            case .Top:
                textView.contentOffset = CGPointZero //set content offset to top
           case .Middle:
                topCorrect = (height - contentHeight * textView.zoomScale)/2.0
                topCorrect = topCorrect < 0 ? 0 : topCorrect
                textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
           case .Bottom:
                topCorrect = textView.bounds.size.height - contentHeight
                topCorrect = topCorrect < 0 ? 0 : topCorrect
                textView.contentOffset = CGPoint(x: 0, y: -topCorrect)
            }
            if contentHeight >= height { //if the contentSize is greater than the height
                topCorrect = contentHeight - height //set the contentOffset to be the
                topCorrect = topCorrect < 0 ? 0 : topCorrect //contentHeight - height of textView
                textView.contentOffset = CGPoint(x: 0, y: topCorrect)
            }
        }
    }

    // MARK: - UIView

    override func layoutSubviews() {
        super.layoutSubviews()
        let size = self.contentSize //forces didSet to be called
        self.contentSize = size
    }
}

В приведенном выше примере (вытащенный непосредственно из моего подкласса) вы заметите, что я широко использую свойство contentOffset. Я делаю некоторые вычисления, чтобы выяснить, где смещение должно быть основано на свойстве вертикального выравнивания, а затем установить свойство смещения содержимого в соответствии с этим (как вы программно прокручиваете с просмотром прокрутки)

Ответ 7

Я использую следующее в приложении, которое автоматически прокручивается вниз при добавлении текста:

Сначала, когда вы инициализируете свой textView, выполните следующие действия:

textView.layoutManager.allowsNonContiguousLayout = false
textView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)

Затем добавьте следующий метод наблюдателя:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    var bottom = textView.contentSize.height - textView.frame.size.height
    if bottom < 0 {
        bottom = 0
    }
    if textView.contentOffset.y != bottom {
        textView.setContentOffset(CGPoint(x: 0, y: bottom), animated: true)
    }
}

параметр allowNonContiguousLayout для ложных фиксированных проблем contentSize для меня.

Добавление наблюдателя contentSize будет наблюдать за любыми новыми изменениями в contentSize textView и вызовом функции -observeValue (forKeyPath...) при внесении изменений.

В функции -observeValue (...) мы сначала получаем дно (y contentOffset при полной прокрутке вниз). Затем мы проверяем, является ли это значение отрицательным, что означает, что высота contentSize меньше высоты кадра textView, и вы не можете действительно прокручивать. Если вы попытаетесь программно прокрутить это отрицательное значение, это вызовет этот печально известный джиттер, который многие знают и любят. Поэтому, чтобы избежать этого дрожания, мы просто устанавливаем значение того, что оно уже должно быть, 0, или вы также можете просто вернуться.

Затем мы просто проверяем, не удовлетворяет ли contentOffset нижним значением, мы даем ему это новое значение. Это позволяет избежать установки contentOffset, когда его не нужно устанавливать.

Ответ 8

Если вы имеете дело с атрибутом UITextView attributedText:

in viewDidLoad()

self.storyTextView.layoutManager.allowsNonContiguousLayout = false

в вашем методе прокрутки

let stringLength:Int = self.storyTextView.attributedText.string.characters.count
self.storyTextView.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))

Ответ 9

Многие люди объясняют, как прокручивать дно, но нужно отметить, что это не сработает, если поместить его в viewDidLoad. Например: мне нужно было использовать это для прокрутки журнала до нижней части страницы при загрузке страницы. Для этого мне пришлось реализовать следующий код

- (void) viewDidLoad {
    [super viewDidLoad];
    [_logTextView setText:[Logger loadLogText]];
}

- (void) viewDidLayoutSubviews {
    [_logTextView
     setContentOffset:CGPointMake(
                          0.0,
                          _logTextView.contentSize.height
                        - _logTextView.frame.size.height
                      )
             animated:NO
    ];
}

Фактическая прокрутка UITextView не может быть выполнена в viewDidLoad.

В моей реализации viewDidLoad я установил текст для текстового поля.

В моей реализации viewDidLayoutSubviews я установил смещение содержимого для UITextView, создав CGPoint, используя высоту содержимого текстовых просмотров минус высота самого текстового представления. Таким образом, когда он прокручивается донизу, нижняя часть текста находится не в верхней части окна, а внизу текст внизу находится в нижней части окна.

Ответ 10

Swift 4

private func textViewScrollToBottom() {
    let bottomRange = NSMakeRange(self.myTextView.text.count - 1, 1)

    self.myTextView.scrollRangeToVisible(bottomRange)
}