Як змусити UITextView прокручуватися до верху щоразу, коли я змінюю текст?


100

Гаразд, у мене є проблеми з UITextView. Ось питання:

Я додаю текст до UITextView. Потім користувач двічі клацає, щоб вибрати щось. Потім я змінюю текст у UITextView(програмно як вище) і UITextView прокручується до нижньої частини сторінки, де є курсор.

Однак, це НЕ, де користувач натиснув. ВИНАГО прокручується донизу UITextViewнезалежно від того, де користувач натиснув.

Отже, ось моє запитання: як я змушую UITextViewпрокручувати до вершини щоразу, коли змінюю текст? Я спробував contentOffsetі scrollRangeToVisible. Ні робота.

Будь-які пропозиції будуть вдячні.

Відповіді:


148
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 ()
— arcady bob

Я додав перегляд тексту в комірці таблиці, тому я встановлюю це в комірці для рядка, але не працює
— Чандні

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))

дякую, жодне з перерахованого вище не працювало для мене. Ваш зразок зробив.
— користувач1244109

Це працює, але ми отримуємо анімацію прокрутки, яку потрібно вимкнути, я віддаю перевагу вимкнення прокрутки та повторне включення, як @miguel запропонував
— Warpzit

Objective-C: [myTextView scrollRangeToVisible: NSMakeRange (0, 0)];
— Державний

Схоже, це більше не працює. iOS 9 та Xcode 7.3.1: /
— wasimsandhu

Swift 3 Stll працює! :) просто скопіюйте цей рядок у своєму методі UITextFielDelegate. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (місцезнаходження: 0, довжина: 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 з атрибутивним рядком. У переглядіWillAppear - не працює
— Тіна Ж

Я згоден з @robotspacer. і це рішення все ще єдине, що працює для мене.
— lerp90

Для мене це рішення працювало лише з короткими (приписуваними) текстами. Якщо у вас є довші тексти, помилка прокрутки UITextView продовжує залишатися в живих :-) Моїм (трохи некрасивим) рішенням було використання dispatch_after із затримкою на 2 секунди, щоб повторно ввімкнути прокрутку. Здається, UITextView потрібно невеликий проміжок часу, щоб зробити його макет тексту ...
— LaborEtArs

38

Дзвінок

scrollRangeToVisible(NSMakeRange(0, 0))

працює, але зателефонуйте

override func viewDidAppear(animated: Bool)

4
Я визнаю, що це працювало на мене лише в viewDidAppear.
— eric f.

2
Це єдине, що працює для мене. Дякую. Але як ви відключите анімацію, яку мені трохи дратує?
— рокхеммер

Хоча це працює, рішення I @Maria краще, тому що це не показує короткого "стрибка".
— Sipke Schoorstra

1
Я повертаю свій останній коментар назад; це найкраща відповідь для мене, оскільки вона працює як на iOS 10, так і на iOS 11, тоді як відповідь Марії не працює добре на iOS 11.
— Sipke Schoorstra

@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 працює як на iOS 10, так і на iOS 11.
— Sipke 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 = НІ; до подання, що містить UITextView, щоб змусити його повністю працювати.
— jengelsma

Я працював лише в SWIFT 3, з іншим зміщенням self.textView.contentOffset.yна основі такої відповіді: stackoverflow.com/a/25366126/1736679
— Efren

Я працював для мене, коли я адаптувався до останнього синтаксису Swift 3
— hawmack13

повністю згоден, наскільки це божевільний? дякую за рішення
— ajonno

17

Я не впевнений, чи розумію ваше запитання, але ви намагаєтесь просто прокрутити погляд до вершини? Якщо так, ви повинні зробити

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


1
@SamStewart Посилання, начебто, порушено, завжди веде на сторінку моїх акаунтів (уже ввійшов у систему), чи можете ви надати більше інформації про те, яке рішення вони там пропонують?
— uliwitness

@uliwitness Це супер стара інформація тепер, я рекомендую спробувати більш сучасний ресурс замість 8 - річного поста. API iOS кардинально змінився за ці 8 років.
— Бенджамін Суссман

1
Я намагався знайти новішу інформацію. Якщо ви знаєте, де я його можу знайти, я би це вдячний. Все ще здається, що це те саме питання :(
— uliwitness

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.


Ідеальне рішення для наслідування!
— iChirag

У мене виникає проблема прокрутки з UITextView в UIView, контрольованому UIPageViewController. (На цих сторінках відображаються екрани довідки / про мене.) Ваше рішення працює для кожної сторінки, крім першої. Перехід на будь-яку сторінку та назад на першу сторінку виправляє положення прокрутки цієї першої сторінки. Я спробував додати ще один .isScrollEnabled = False у viewWillAppear, але це не мало ефекту. Мені натоптано, чому перша сторінка поводиться інакше, ніж решта.
— Уейн Хендерсон

[оновлення] Просто вирішено! Введення затримки на 0,5 секунди до .isScrollEnabled = справжні тригери усуває проблему першого завантаження.
— Уейн Хендерсон

дуже хороша . . .
— Maysam R

Працював як шарм. Єдине рішення, яке працює для мене. Ти чоловік.
— Ледачий ніндзя

10

У мене є оновлена ​​відповідь, згідно з якою перегляд тексту буде відображатися належним чином і без того, щоб користувач не відчував анімацію прокрутки.

override func viewWillAppear(animated: Bool) {
    dispatch_async(dispatch_get_main_queue(), {
        self.introductionText.scrollRangeToVisible(NSMakeRange(0, 0))
    })

Чому ви робите в основній черзі, sine viewWillAppear працює на основній черзі?
— karthikPrabhu Alagu

1
Занадто багато місяця тому, щоб згадати, але тут зверталися: stackoverflow.com/a/17490329/4750649
— MacInnis

8

Ось як це працювало в iOS 9 Release, щоб текстView прокручувався вгорі, перш ніж з'являтися на екрані

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

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    textView.scrollEnabled = YES;
}

1
Це зробило трюк. Між іншим, це навчило мене чомусь дуже важливому: "погляд" у "viewwillappear" стосується не лише перегляду себе, але, очевидно, ВСІХ поглядів. Немає?
— Sjakelien

8

Ось як я це зробив

Швидкий 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 ???
— Nicolas Miari

2
Мені також було цікаво, чому я маю чітко відправляти по головній черзі, хоча я виконую всі події на головній черзі. Але це працює для мене
— Анкіта Garg

6

Спробуйте це перемістити курсор у верхню частину тексту.

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

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


спробував це людина, не пощастило. Здається, це щось із першим Відповідачем. Будь-які інші пропозиції?
— Сем Стюарт

у мене така ж проблема з положенням курсору, він працював для мене ... великий +1 для цього.
— Дхаваль

У мене є анімація знизу вгору, але я не вимагав цього, як це видалити.
— Дхаваль

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
}

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.
— Йорданія H

Вам потрібно буде замінити NSRange (0,0) на NSMakeRange (0,0)
— RobP


5

просто ви можете використовувати цей код

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    textView.scrollRangeToVisible(NSMakeRange(0, 0))
}

4

Відповідь Swift 2:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Це запобігає прокрученню textView до кінця тексту після його встановлення.


4

У Swift 3:

  • viewWillLayoutSubviews - це місце для внесення змін. viewWillAppear також повинен працювати, але логічно, макет повинен виконуватись у viewWillLayoutSubviews .

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

Припустимо, UITextView названий як вашUITextView . Ось код:

// 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

Це працювало для мене. Він заснований на відповіді RyanTCB, але це варіант 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]; НЕ працює іноді (я не знаю чому), тому скоріше скористайтеся прокручуваноюпоміткою.

3

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

Я просто використав такий рядок:

displayText.contentOffset.y = 0

1
Працювали (iOS 10.3)! Я додав його всередину, viewDidLayoutSubviewsщоб це сталося, коли екран обертається. В іншому випадку зміщення змісту стає нерівним.
— Щеня


2

У мене виникла проблема, але в моєму випадку textview - це перегляд певного користувацького перегляду і додав його до 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
}

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 прокручує до кінця тексту, а потім прокручуєRangeToVisible прокручує до значення scrollToLocation. Тим самим потрібна позиція відображається в першому рядку scrollView.


1

Для мене в iOS 9 він перестав працювати для мене з атрибутивним рядком методом scrollRangeToVisible (1 рядок був жирним шрифтом, інші рядки були звичайним шрифтом)

Тому мені довелося використовувати:

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()
      }
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.