Як змусити 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.