Как заставить UITextView прокручиваться вверх каждый раз, когда я меняю текст?

100

Хорошо, у меня проблема с файлом UITextView. Вот в чем проблема:

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

Однако это НЕ то место, где пользователь щелкнул. Он ВСЕГДА прокручивается до конца UITextViewнезависимо от того, где щелкнул пользователь.

Итак, вот мой вопрос: как заставить UITextViewпрокрутку вверх каждый раз, когда я меняю текст? Я пробовал contentOffsetи scrollRangeToVisible. Ни работать.

Мы ценим любые предложения.

Сэм Стюарт
источник

Ответы:

149
UITextView*note;
[note setContentOffset:CGPointZero animated:YES];

Это делает это за меня.

Версия Swift (Swift 4.1 с iOS 11 на Xcode 9.3):

note.setContentOffset(.zero, animated: true)
Уэйн Ло
источник
13
Мне это не поможет.
Дмитрий
6
Это работает, но зачем это вообще нужно? Логика прокрутки Apple нуждается в доработке, слишком много прыжков через обруч, чтобы заставить вещи вести себя так, как они должны автоматически в большинстве случаев.
Джон Контарино
4
У меня работает в iOS 9, но не ранее viewDidAppear().
Мюррей Сагал
4
Вы можете вызвать это раньше, чем viewDidAppear (), вызвав его в viewDidLayoutSubviews ()
аркадий боб
Я добавил текстовое представление в ячейку таблицы, поэтому я устанавливаю это в ячейке для строки, но не работаю
Чандни
67

Если кто имеет эту проблему прошивки 8, я обнаружил , что просто установив UITextView'sсвойство текста, то вызов scrollRangeToVisibleс NSRangeс location:0, length:0работал. Мое текстовое представление было недоступно для редактирования, и я протестировал как возможность выбора, так и отсутствие возможности выбора (ни один параметр не повлиял на результат). Вот пример Swift:

myTextView.text = "Text that is long enough to scroll"
myTextView.scrollRangeToVisible(NSRange(location:0, length:0))
nerd2know
источник
спасибо, ничего из вышеперечисленного у меня не сработало. Ваш образец сделал.
user1244109
Это работает, но мы получаем анимацию прокрутки, которую нужно отключить, я предпочитаю отключать прокрутку и повторно включать ее, как предложил
@miguel
Цель-C: [myTextView scrollRangeToVisible: NSMakeRange (0, 0)];
Stateful
Кажется, это больше не работает. iOS 9 и Xcode 7.3.1: /
wasimsandhu
Swift 3 Stll работает! :) просто скопируйте эту строку в свой метод UITextFielDelegate. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (location: 0, length: 0))}`
lifewithelliott
46

И вот мое решение ...

    override func viewDidLoad() {
        textView.scrollEnabled = false
        textView.text = "your text"
    }

    override func viewDidAppear(animated: Bool) {
        textView.scrollEnabled = true
    }
Мигель
источник
5
Мне не нравится это решение, но, по крайней мере, в iOS 9 beta 5 это единственное, что у меня работает. Я предполагаю, что большинство других решений предполагают, что ваше текстовое представление уже видно. В моем случае это не всегда так, поэтому мне приходится отключать прокрутку до тех пор, пока она не появится.
robotspacer
1
Это было единственное, что у меня тоже сработало. iOS 9 с использованием нередактируемого текстового представления.
SeanR
1
Это работает для меня, iOS 8, не редактируемое текстовое представление с приписанной строкой. В viewWillAppear - не работает
Тина Ж
Я согласен с @robotspacer. и это решение до сих пор единственное, что мне подходит.
lerp90
Для меня это решение работало только с короткими (атрибутированными) текстами. При наличии более длинных текстов ошибка прокрутки UITextView продолжает оставаться в живых :-) Мое (немного уродливое) решение заключалось в использовании dispatch_after с 2-секундной задержкой для повторного включения прокрутки. UITextView, похоже, нужно немного времени, чтобы сделать свой текстовый макет ...
LaborEtArs,
38

Вызов

scrollRangeToVisible(NSMakeRange(0, 0))

работает, но позвони

override func viewDidAppear(animated: Bool)
RyanTCB
источник
4
Я подтверждаю, что у меня это сработало только в viewDidAppear.
Эрик Ф.
2
Это единственный, который мне подходит. Спасибо. Но как отключить анимацию, которая меня немного раздражает?
rockhammer 04
Хотя это работает, решение I @Maria лучше, потому что оно не показывает краткого «скачка».
Сипка Schoorstra
1
Беру свой последний комментарий; это лучший ответ для меня, так как он работает как на iOS 10, так и на iOS 11, тогда как ответ
Сипке
@SipkeSchoorstra, см. Мой ответ ниже, чтобы он работал без скачков (с использованием KVO).
Терри
29

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

[yourTextView setScrollEnabled:NO];
yourTextView.text = yourtext;
[yourTextView setScrollEnabled:YES];
Мария
источник
У меня был textView внутри tableViewCell. В этом случае textView прокручивается до прибл. 50%. Это решение настолько просто, насколько логично. Спасибо
Джулиан Ф. Вайнерт
1
@ JulianF.Weinert Похоже, это не работает для iOS10. У меня такая же ситуация, как у вас, с textView в tableViewCell. Я не использую возможности редактирования textView. Мне просто нужен прокручиваемый текст. Но textView всегда прокручивается примерно до 50%, как вы сказали. Я также пробовал все другие предложения по установке setContentOffset и scrollRangeToVisible в этом сообщении. Ни одной работы. Вы могли сделать что-нибудь еще, чтобы заставить эту работу работать?
JeffB6688
@ JeffB6688 извини, нет. Это действительно очень давно ... Может быть, это еще одна причуда «нового» релиза iOS?
Джулиан Ф. Вайнерт
Но, к сожалению, не идеально на iOS 11.2. Ответ работа Onlu @RyanTCB «s на оба прошивке 10 и 11. прошивки
Сипке Schoorstra
Пока что у меня это отлично работает на iOS 9, 10 и 11, но мне пришлось включить его viewDidAppearкак часть более широкого серьезного исправления
MadProgrammer
19

Поскольку ни одно из этих решений у меня не сработало, и я потратил слишком много времени, собирая решения по кусочкам, это наконец решило проблему для меня.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        let desiredOffset = CGPoint(x: 0, y: -self.textView.contentInset.top)
        self.textView.setContentOffset(desiredOffset, animated: false)
    })
}

Это действительно глупо, что это не поведение по умолчанию для этого элемента управления.

Надеюсь, это поможет кому-то другому.

Дрю С.
источник
2
Это было очень полезно! Мне также пришлось добавить: self.automaticallyAdjustsScrollViewInsets = NO; в представление, содержащее UITextView, чтобы заставить его полностью работать.
jengelsma
Я заставил его работать только в SWIFT 3, с другим расчетом смещения self.textView.contentOffset.yна основе этого ответа: stackoverflow.com/a/25366126/1736679
Эфрен
Сработало для меня, когда я адаптировал последний синтаксис Swift 3
hawmack13,
полностью согласен, насколько это безумие? спасибо за решение
ajonno 07
18

Я не уверен, понял ли я ваш вопрос, но вы пытаетесь просто прокрутить представление наверх? Если да, тебе следует сделать

[textview scrollRectToVisible:CGRectMake(0,0,1,1) animated:YES];

Бенджамин Сассман
источник
1
@SamStewart Ссылка кажется неработающей, всегда ведет на страницу моих учетных записей (уже авторизованных), можете ли вы предоставить дополнительную информацию о том, какое решение они там предлагают?
uliwitness
@uliwitness Это очень старая информация, я бы порекомендовал попробовать более современный ресурс вместо 8-летней давности. API iOS кардинально изменился за эти 8 лет.
Benjamin Sussman
1
Я пытался найти более новую информацию. Если вы знаете, где я могу его найти, я был бы признателен. По-прежнему, похоже, та же проблема :(
uliwitness
Спасибо, это сработало для меня, потому что я также установил вставки в определяемых пользователем атрибутах времени выполненияTextView.scrollRectToVisible(CGRect(x: 0,y: 0,width: 1,height: 1), animated: false)
Курт Лейн
13

Для iOS 10 и Swift 3 я сделал:

override func viewDidLoad() {
    super.viewDidLoad()
    textview.isScrollEnabled = false
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    textView.isScrollEnabled = true
}

Сработало для меня, не уверен, что у вас проблема с последней версией Swift и iOS.

ChallengerGuy
источник
Идеальное решение для подражания!
iChirag
У меня проблема с прокруткой с UITextView в UIView, управляемом UIPageViewController. (На этих страницах показаны мои экраны «Справка» / «О программе».) Ваше решение работает для всех страниц, кроме первой. Перелистывание на любую страницу и обратно на первую страницу фиксирует положение прокрутки этой первой страницы. Я попытался добавить еще один .isScrollEnabled = False в viewWillAppear, но это не повлияло. Я не понимаю, почему первая страница ведет себя иначе, чем остальные.
Уэйн Хендерсон
[обновление] Просто решил! Введение задержки в 0,5 секунды перед триггерами .isScrollEnabled = true устраняет проблему первой загрузки.
Уэйн Хендерсон
очень хорошо . . .
Maysam R
Работал как шарм. Единственное решение, которое у меня работает. Ты человек.
Ленивый ниндзя,
10

У меня есть обновленный ответ, в котором текстовое представление будет отображаться правильно и без анимации прокрутки у пользователя.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        self.introductionText.scrollRangeToVisible(NSMakeRange(0, 0))
    })
Макиннис
источник
Почему вы делаете в основной очереди, синус viewWillAppear работает в основной очереди?
karthikPrabhu Alagu
1
Слишком много лун назад для меня вспомнить , но его имя здесь: stackoverflow.com/a/17490329/4750649
Macinnis
8

Вот как это работало в выпуске iOS 9, так как textView прокручивается сверху, прежде чем появится на экране.

- (void)viewDidLoad
{
    [super viewDidLoad];
    textView.scrollEnabled = NO;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    textView.scrollEnabled = YES;
}
Ageorgios
источник
1
Это сработало. Между прочим, это научило меня кое-чему очень важному: «view» в «viewwillappear» относится не только к self.view, но, по-видимому, ко ВСЕМ представлениям. Нет?
Sjakelien
8

Вот как я это сделал

Swift 4:

extension UITextView {

    override open func draw(_ rect: CGRect)
    {
        super.draw(rect)
        setContentOffset(CGPoint.zero, animated: false)
    }

 }
Максим Горбенко
источник
Это единственный метод, который дает мне наилучшие результаты. Я поддерживаю.
smoothBlue
7

Это сработало для меня

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    DispatchQueue.main.async {
      self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
    }
  }
Анкит Гарг
источник
Это очень странно. На большинстве устройств перенос вызова DispatchQueue.main.async(...не требуется, но по какой-то причине (например, на iPhone 5s (iOS 12.1)) он не работает, если вы не отправите вызов . Но, как сообщает стек вызовов отладчика, во всех случаях viewWillAppear() уже выполняется основная очередь ... - WTF, Apple ???
Николас Миари
2
Мне также было интересно, почему я должен отправлять явно в основной очереди, даже если я запускаю все события в основной очереди. Но у меня это сработало
Анкит Гарг
6

Попробуйте переместить курсор в верхнюю часть текста.

NSRange r  = {0,0};
[yourTextView setSelectedRange:r];

Посмотрите, как это происходит. Убедитесь, что вы вызываете это после того, как все ваши события активированы или все, что вы делаете, будет сделано.

Джон Баллинджер
источник
попробовал человек, не повезло. Вроде что-то с firstResponder. Есть другие предложения?
Сэм Стюарт,
у меня такая же проблема с позицией курсора, у меня это сработало ... отлично +1 для этого.
Дхавал
У меня есть анимация снизу вверх, но мне это не требовалось, так как это удалить.
Dhaval
2
Он не прокручивается вверх (меньше на несколько пикселей).
Дмитрий
6

Моя проблема заключается в том, чтобы установить textView для прокрутки вверх при появлении представления. Для iOS 10.3.2 и Swift 3.

Решение 1:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // It doesn't work. Very strange.
    self.textView.setContentOffset(CGPoint.zero, animated: false)
}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // It works, but with an animation effection.
    self.textView.setContentOffset(CGPoint.zero, animated: false)
}

Решение 2:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // It works.       
    self.textView.contentOffset = CGPoint.zero
}
AechoLiu
источник
5

Объединил предыдущие ответы, теперь он должен работать:

talePageText.scrollEnabled = false
talePageText.textColor = UIColor.blackColor()
talePageText.font = UIFont(name: "Bradley Hand", size: 24.0)
talePageText.contentOffset = CGPointZero
talePageText.scrollRangeToVisible(NSRange(location:0, length:0))
talePageText.scrollEnabled = true
Геза Микло
источник
lmao единственное, что у меня работало как глупо. Однако я смог удалить scrollRangeToVisible.
Jordan H
Вам нужно будет заменить NSRange (0,0) на NSMakeRange (0,0)
RobP
5
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

Эта строка кода мне подходит.

Патрисия
источник
5

просто вы можете использовать этот код

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    textView.scrollRangeToVisible(NSMakeRange(0, 0))
}
Erkancan
источник
4

Swift 2 ответ:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Это предотвращает прокрутку textView до конца текста после его установки.

Glace
источник
4

В Swift 3:

  • viewWillLayoutSubviews - это место для внесения изменений. viewWillAppear также должен работать, но по логике макет должен выполняться в viewWillLayoutSubviews .

  • Что касается методов, то будут работать как scrollRangeToVisible, так и setContentOffset . Однако setContentOffset позволяет включать или отключать анимацию.

Предположим, что UITextView назван yourUITextView . Вот код:

// Connects to the TextView in the interface builder.
@IBOutlet weak var yourUITextView: UITextView!

/// Overrides the viewWillLayoutSubviews of the super class.
override func viewWillLayoutSubviews() {

    // Ensures the super class is happy.
    super.viewWillLayoutSubviews()

    // Option 1:
    // Scrolls to a range of text, (0,0) in this very case, animated.
    yourUITextView.scrollRangeToVisible(NSRange(location: 0, length: 0))

    // Option 2:
    // Sets the offset directly, optional animation.
    yourUITextView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
}
Марко Леонг
источник
viewWillLayoutSubviews вызывается при любом изменении размера представления. Обычно это не то, что вам нужно.
uliwitness
4

Мне пришлось вызвать следующее внутри viewDidLayoutSubviews (вызывать внутри viewDidLoad было слишком рано):

    myTextView.setContentOffset(.zero, animated: true)
Чарли Селигман
источник
3

Это сработало для меня. Он основан на ответе RyanTCBs, но это вариант того же решения для Objective-C:

- (void) viewWillAppear:(BOOL)animated {
    // For some reason the text view, which is a scroll view, did scroll to the end of the text which seems to hide the imprint etc at the beginning of the text.
    // On some devices it is not obvious that there is more text when the user scrolls up.
    // Therefore we need to scroll textView to the top.
    [self.textView scrollRangeToVisible:NSMakeRange(0, 0)];
}
Герман Клекер
источник
3
-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    [textview setContentOffset:CGPointZero animated:NO];
}
Альберт Цзоу
источник
viewWillLayoutSubviews вызывается при любом изменении размера представления. Обычно это не то, что вам нужно.
uliwitness
3
-(void) viewDidAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappearсделает это мгновенно, прежде чем пользователь заметит, но ViewDidDisappearраздел будет лениво анимировать.
  • [mytextView setContentOffset:CGPointZero animated:YES]; иногда НЕ работает (я не знаю почему), поэтому лучше используйте scrollrangetovisible.
Канг
источник
3

Проблема решена: iOS = 10.2 Swift 3 UITextView

Я просто использовал следующую строку:

displayText.contentOffset.y = 0
Стивен Роу
источник
1
Работает (iOS 10.3)! Я добавил его внутрь, viewDidLayoutSubviewsчтобы это происходило при повороте экрана. В противном случае смещение содержимого станет смещенным.
Pup
2
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];
Шайла
источник
Пожалуйста, отредактируйте с более подробной информацией.
Schemetrical
2

У меня была проблема, но в моем случае текстовое представление является подвидом некоторого настраиваемого представления и добавлено его в ViewController. Ни одно решение не помогло мне. Для решения проблемы добавлена ​​задержка 0,1 сек, а затем включена прокрутка. У меня это сработало.

textView.isScrollEnabled = false
..
textView.text = // set your text here

let dispatchTime = DispatchTime.now() + 0.1
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
   self.textView.isScrollEnabled = true
}
SaRaVaNaN DM
источник
1

Для меня отлично работает этот код:

    textView.attributedText = newText //or textView.text = ...

    //this part of code scrolls to top
    textView.contentOffset.y = -64
    textView.scrollEnabled = false
    textView.layoutIfNeeded() //if don't work, try to delete this line
    textView.scrollEnabled = true

Для прокрутки до точного положения и отображения его в верхней части экрана я использую этот код:

    var scrollToLocation = 50 //<needed position>
    textView.contentOffset.y = textView.contentSize.height
    textView.scrollRangeToVisible(NSRange.init(location: scrollToLocation, length: 1))

Установка contentOffset.y прокручивает до конца текста, а затем scrollRangeToVisible прокручивает до значения scrollToLocation. Таким образом, нужная позиция появляется в первой строке scrollView.

Игорь
источник
1

Для меня в iOS 9 он перестал работать для меня с атрибутной строкой с методом scrollRangeToVisible (первая строка была с жирным шрифтом, другие строки были с обычным шрифтом)

Поэтому мне пришлось использовать:

textViewMain.attributedText = attributedString
textViewMain.scrollRangeToVisible(NSRange(location:0, length:0))
delay(0.0, closure: { 
                self.textViewMain.scrollRectToVisible(CGRectMake(0, 0, 10, 10), animated: false)
            })

где задержка:

func delay(delay:Double, closure:()->Void) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}
Пол Т.
источник
1

Попробуйте использовать это двухстрочное решение:

view.layoutIfNeeded()
textView.contentOffset = CGPointMake(textView.contentInset.left, textView.contentInset.top)
Николай Шубенков
источник
Это сработало для меня. Мой UITextView находился внутри UICollectionVewCell. Я подозреваю, что scrollRangeToVisible прокручивается не в том месте, если макет не рассчитан должным образом
Джон Фаулер,
1

Вот мои коды. Работает нормально.

class MyViewController: ... 
{
  private offsetY: CGFloat = 0
  @IBOutlet weak var textView: UITextView!
  ...

  override viewWillAppear(...) 
  {
      ...
      offsetY = self.textView.contentOffset.y
      ...
  }
  ...
  func refreshView() {
      let offSetY = textView.contentOffset.y
      self.textView.setContentOffset(CGPoint(x:0, y:0), animated: true)
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
        [unowned self] in
        self.textView.setContentOffset(CGPoint(x:0, y:self.offSetY), 
           animated: true)
        self.textView.setNeedsDisplay()
      }
  }
David.Chu.ca
источник