Bagaimana cara memaksa UITextView untuk menggulir ke atas setiap kali saya mengubah teks?


100

Oke, saya mengalami masalah dengan UITextView. Inilah masalahnya:

Saya menambahkan beberapa teks ke UITextView. Pengguna kemudian mengklik dua kali untuk memilih sesuatu. Saya kemudian mengubah teks di UITextView(secara programatik seperti di atas) dan UITextView gulungan ke bagian bawah halaman di mana ada kursor.

Namun, itu BUKAN saat pengguna mengklik. Itu SELALU menggulir ke bawah UITextViewterlepas dari mana pengguna mengklik.

Jadi, inilah pertanyaan saya: Bagaimana cara memaksa UITextViewuntuk menggulir ke atas setiap kali saya mengubah teks? Saya sudah mencoba contentOffsetdan scrollRangeToVisible. Tidak ada yang berhasil.

Setiap saran akan dihargai.

Jawaban:


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

Ini melakukannya untuk saya.

Versi Swift (Swift 4.1 dengan iOS 11 pada Xcode 9.3):

note.setContentOffset(.zero, animated: true)

13
Tidak membantu saya.
Dmitry

6
Ini berhasil, tetapi mengapa ini tetap perlu? Logika pengguliran Apple membutuhkan penyempurnaan, ada terlalu banyak lompatan lingkaran untuk membuat segala sesuatunya berperilaku seperti seharusnya secara otomatis dalam banyak kasus.
John Contarino

4
Berfungsi untuk saya di iOS 9 tetapi tidak lebih awal dari viewDidAppear().
Murray Sagal

4
Anda bisa memanggil ini lebih awal dari viewDidAppear () dengan memanggilnya di viewDidLayoutSubviews ()
arcady bob

Saya telah menambahkan tampilan teks di sel tabel jadi saya mengatur ini di sel untuk baris tetapi tidak berfungsi
Chandni

67

Jika seseorang memiliki masalah ini di iOS 8, saya menemukan bahwa hanya pengaturan UITextView'sproperti teks, kemudian memanggil scrollRangeToVisibledengan NSRangedengan location:0, length:0, bekerja. Tampilan teks saya tidak dapat diedit, dan saya menguji keduanya dapat dipilih dan tidak dapat dipilih (tidak ada pengaturan yang memengaruhi hasil). Berikut contoh Swift:

myTextView.text = "Text that is long enough to scroll"
myTextView.scrollRangeToVisible(NSRange(location:0, length:0))

terima kasih, semua hal di atas tidak berhasil untuk saya. Sampel Anda melakukannya.
pengguna1244109

Ini berfungsi, tetapi kami mendapatkan animasi gulir yang perlu dinonaktifkan, saya lebih suka menonaktifkan gulir dan mengaktifkan kembali seperti yang disarankan
@miguel

Objective-C: [myTextView scrollRangeToVisible: NSMakeRange (0, 0)];
Stateful

Ini sepertinya tidak berfungsi lagi. iOS 9 dan Xcode 7.3.1: /
wasimsandhu

Swift 3 Stll bekerja! :) cukup salin baris ini dalam metode UITextFielDelegate Anda. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (location: 0, length: 0))}`
lifewithelliott

46

Dan inilah solusiku ...

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

    override func viewDidAppear(animated: Bool) {
        textView.scrollEnabled = true
    }

5
Saya tidak menyukai solusi ini, tetapi setidaknya pada iOS 9 beta 5, itu satu-satunya hal yang berhasil untuk saya. Saya berasumsi sebagian besar solusi lain mengasumsikan tampilan teks Anda sudah terlihat. Dalam kasus saya itu tidak selalu benar, jadi saya harus terus menggulir dinonaktifkan sampai muncul.
robotspacer

1
Ini adalah satu-satunya hal yang berhasil untuk saya juga. iOS 9, menggunakan textview yang tidak dapat diedit.
SeanR

1
Ini berfungsi untuk saya, textview iOS 8 yang tidak dapat diedit dengan string yang diatribusikan. Dalam viewWillAppear - tidak bekerja
Tina Zh

Saya setuju dengan @robotspacer. dan solusi ini masih satu-satunya yang berhasil untuk saya.
lerp90

Bagi saya, solusi ini hanya berfungsi dengan teks pendek (dikaitkan). Saat memiliki teks yang lebih panjang, bug scroll UITextView tetap hidup :-) Solusi saya (sedikit jelek) adalah menggunakan dispatch_after dengan penundaan 2 detik untuk mengaktifkan kembali scrolling. UITextView tampaknya membutuhkan sedikit waktu untuk melakukan tata letak teksnya ...
LaborEtArs

38

Panggilan

scrollRangeToVisible(NSMakeRange(0, 0))

berfungsi tetapi panggil

override func viewDidAppear(animated: Bool)

4
Saya yakin bahwa ini hanya berfungsi untuk saya di viewDidAppear.
eric f.

2
Ini adalah satu-satunya yang berhasil untuk saya. Terima kasih. Tetapi bagaimana Anda menonaktifkan animasi yang menurut saya sedikit mengganggu?
rockhammer

Meskipun ini berhasil, solusi I @Maria lebih baik, karena solusi itu tidak menunjukkan "lompatan" singkat.
Sipke Schoorstra

1
Saya menarik kembali komentar terakhir saya; ini adalah jawaban terbaik untuk saya karena berfungsi di iOS 10 dan iOS 11, sedangkan jawaban @ Maria tidak berfungsi dengan baik di iOS 11.
Sipke Schoorstra

@SipkeSchoorstra, lihat jawaban saya di bawah ini untuk membuatnya bekerja tanpa lompatan (menggunakan KVO).
Terry

29

Saya menggunakan atributedString dalam HTML dengan tampilan teks tidak dapat diedit. Mengatur offset konten juga tidak berhasil untuk saya. Ini berhasil untuk saya: nonaktifkan gulir diaktifkan, atur teks, lalu aktifkan pengguliran lagi

[yourTextView setScrollEnabled:NO];
yourTextView.text = yourtext;
[yourTextView setScrollEnabled:YES];

Saya memiliki textView di dalam tableViewCell. Dalam hal ini textView digulir ke kira-kira. 50%. Solusi ini sesederhana logis. Terima kasih
Julian F. Weinert

1
@ JulianF. Weinert Sepertinya ini tidak berfungsi untuk iOS10. Saya memiliki situasi yang sama seperti Anda dengan textView di tableViewCell. Saya tidak menggunakan kemampuan pengeditan textView. Saya hanya ingin teks yang dapat digulir. Tapi textView selalu di-scroll ke sekitar 50% seperti yang Anda katakan. Saya juga mencoba semua saran lain dengan menyetel setContentOffset dan scrollRangeToVisible di posting ini. Tidak ada yang berhasil. Apakah ada hal lain yang mungkin telah Anda lakukan untuk membuat ini berhasil?
JeffB6688

@ JeffB6688 maaf, tidak. Itu benar-benar sudah lama sekali ... Mungkinkah hal unik lain dari rilis iOS "baru"?
Julian F. Weinert

Tapi sayangnya tidak sempurna di iOS 11.2. Jawaban Onlu @RyanTCB berfungsi di iOS 10 dan iOS 11.
Sipke Schoorstra

Ini berfungsi dengan baik untuk saya sejauh ini di iOS 9, 10 dan 11, tetapi saya harus mengaktifkannya viewDidAppearsebagai bagian dari perbaikan serius yang lebih luas
MadProgrammer

19

Karena tidak ada solusi ini yang berhasil untuk saya dan saya membuang terlalu banyak waktu untuk mengumpulkan solusi, ini akhirnya menyelesaikannya untuk saya.

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

Ini benar-benar konyol bahwa ini bukan perilaku default untuk kontrol ini.

Saya harap ini membantu orang lain.


2
Ini sangat membantu! Saya juga harus menambahkan: self.automaticallyAdjustsScrollViewInsets = NO; ke tampilan yang berisi UITextView agar berfungsi sepenuhnya.
jengelsma

Saya hanya membuatnya berfungsi di SWIFT 3, dengan perhitungan offset lain self.textView.contentOffset.yberdasarkan jawaban ini: stackoverflow.com/a/25366126/1736679
Efren

Bekerja untuk saya setelah saya beradaptasi dengan sintaks terbaru untuk Swift 3
hawmack13

setuju betapa gilanya ini? terima kasih atas solusinya
ajonno

17

Saya tidak yakin apakah saya memahami pertanyaan Anda, tetapi apakah Anda hanya mencoba menggulir tampilan ke atas? Jika demikian, Anda harus melakukannya

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


1
@SamStewart Tautan sepertinya rusak, selalu mengarah ke halaman akun saya (sudah masuk) dapatkah Anda memberikan info lebih lanjut tentang solusi apa yang mereka tawarkan di sana?
uliwitness

@uliwitness Sekarang ini adalah informasi yang sangat lama, saya akan merekomendasikan untuk mencoba sumber daya yang lebih modern daripada posting berusia 8 tahun. API iOS telah berubah secara dramatis dalam 8 tahun tersebut.
Benjamin Sussman

1
Saya telah mencoba mencari info terbaru. Jika Anda tahu di mana saya bisa menemukannya, saya akan menghargainya. Sepertinya masih masalah yang sama :(
uliwitness

13

Untuk iOS 10 dan Swift 3 saya melakukan:

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

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

Berhasil untuk saya, tidak yakin apakah Anda mengalami masalah dengan versi terbaru Swift dan iOS.


Solusi sempurna untuk diikuti!
iChirag

Saya mengalami masalah pengguliran dengan UITextView di UIView yang dikendalikan oleh UIPageViewController. (Halaman ini menunjukkan layar Bantuan / Tentang saya.) Solusi Anda berfungsi untuk setiap halaman kecuali yang pertama. Menggeser ke halaman mana pun dan kembali ke halaman pertama memperbaiki posisi gulir halaman pertama tersebut. Saya mencoba menambahkan lagi .isScrollEnabled = False ke viewWillAppear tetapi ini tidak berpengaruh. Saya bingung mengapa halaman pertama berperilaku berbeda dari yang lain.
Wayne Henderson

[update] Baru saja menyelesaikannya! Memperkenalkan penundaan 0,5 detik sebelum pemicu .isScrollEnabled = true memperbaiki masalah pemuatan pertama.
Wayne Henderson

sangat bagus . . .
Maysam R

Bekerja seperti pesona. Satu-satunya solusi yang berhasil untuk saya. Kamulah orangnya.
Ninja Malas

10

Saya memiliki jawaban yang diperbarui yang akan membuat tampilan teks muncul dengan benar, dan tanpa pengguna mengalami animasi gulir.

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

Mengapa Anda melakukan dalam antrian utama, sinus viewWillAppear berjalan di antrian utama?
karthikPrabhu Alagu

1
Terlalu banyak bulan yang lalu untuk saya ingat tetapi ditujukan di sini: stackoverflow.com/a/17490329/4750649
MacInnis

8

Ini adalah cara kerjanya di iOS 9 Release sehingga textView digulir ke atas sebelum muncul di layar

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

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

1
Ini berhasil. Secara kebetulan, ini mengajari saya sesuatu yang sangat penting: 'view' dalam 'viewwillappear' tidak hanya merujuk pada self.view, tetapi tampaknya pada SEMUA tampilan. Tidak?
Sjakelien

8

Begitulah cara saya melakukannya

Cepat 4:

extension UITextView {

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

 }

Ini adalah satu-satunya metode yang memberikan hasil terbaik untuk saya. Saya suka.
smoothBlue

7

Ini berhasil untuk saya

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

    DispatchQueue.main.async {
      self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
    }
  }

Ini sangat aneh. Pada kebanyakan perangkat, menggabungkan panggilan DispatchQueue.main.async(...tidak diperlukan, tetapi untuk beberapa alasan, pada (mis.) IPhone 5s (iOS 12.1) itu tidak berfungsi kecuali Anda mengirim. Tetapi seperti yang dilaporkan oleh tumpukan panggilan pada debugger, viewWillAppear() sudah berjalan di antrian utama dalam semua kasus ... - WTF, Apple ???
Nicolas Miari

2
Saya juga bertanya-tanya mengapa saya harus mengirimkan secara eksplisit di antrean utama meskipun saya menjalankan semua acara di antrean utama. Tapi ini berhasil untuk saya
Ankit garg

6

Coba ini untuk memindahkan kursor ke atas teks.

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

Lihat bagaimana kelanjutannya. Pastikan Anda memanggil ini setelah semua acara Anda dipecat atau apa pun yang Anda lakukan selesai.


mencobanya kawan, tidak berhasil. Tampaknya ada sesuatu dengan firstResponder. Ada saran lain?
Sam Stewart

memiliki masalah yang sama dengan posisi kursor, itu berhasil untuk saya ... +1 yang bagus untuk itu.
Dhaval

Saya telah mendapatkan animasi dari bawah ke atas tetapi saya belum memerlukannya, jadi bagaimana cara menghapusnya.
Dhaval

2
Itu tidak menggulir ke atas (kurang pada beberapa piksel).
Dmitry

6

Masalah saya adalah menyetel textView ke gulir ke atas saat tampilan muncul. Untuk iOS 10.3.2, dan Swift 3.

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

Solusi 2:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    // It works.       
    self.textView.contentOffset = CGPoint.zero
}

5

Menggabungkan jawaban sebelumnya, sekarang seharusnya berfungsi:

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 satu-satunya hal yang berhasil bagiku betapa konyolnya. Saya bisa menghapusnya scrollRangeToVisible.
Jordan H

Anda harus mengganti NSRange (0,0) dengan NSMakeRange (0,0)
RobP

5
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];

Baris kode ini bekerja untuk saya.


5

Anda hanya dapat menggunakan kode ini

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

4

Swift 2 Jawaban:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Ini mencegah textView bergulir ke akhir teks setelah menyetelnya.


4

Di Swift 3:

  • viewWillLayoutSubviews adalah tempat untuk membuat perubahan. viewWillAppear seharusnya berfungsi juga, tetapi secara logis, tata letak harus dilakukan di viewWillLayoutSubviews .

  • Terkait metode, scrollRangeToVisible dan setContentOffset akan berfungsi. Namun, setContentOffset memungkinkan animasi mati atau hidup.

Asumsikan UITextView dinamai sebagai yourUITextView . Berikut kodenya:

// 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 dipanggil pada setiap pengubahan ukuran tampilan. Biasanya bukan itu yang Anda inginkan.
uliwitness

4

Saya harus memanggil dalam viewDidLayoutSubviews (memanggil di dalam viewDidLoad terlalu dini):

    myTextView.setContentOffset(.zero, animated: true)

tidak bekerja di setiap skenario
Raja Saad

3

Ini berhasil untuk saya. Ini didasarkan pada jawaban RyanTCB tetapi merupakan varian Objective-C dari solusi yang sama:

- (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 dipanggil pada setiap pengubahan ukuran tampilan. Biasanya bukan itu yang Anda inginkan.
uliwitness

3
-(void) viewDidAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappearakan melakukannya secara instan sebelum pengguna dapat menyadarinya, tetapi ViewDidDisappearbagian akan menghidupkannya dengan malas.
  • [mytextView setContentOffset:CGPointZero animated:YES]; kadang-kadang TIDAK berfungsi (saya tidak tahu mengapa), jadi gunakan scrollrangetovisible.

3

Masalah terpecahkan: iOS = 10.2 Swift 3 UITextView

Saya baru saja menggunakan baris berikut:

displayText.contentOffset.y = 0

1
Berhasil (iOS 10.3)! Saya menambahkannya di dalam viewDidLayoutSubviewssehingga terjadi saat layar berputar. Jika tidak, offset konten menjadi tidak sejajar.
Pup

2
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];

Harap edit dengan lebih detail.
Skema

2

Saya punya masalah, tetapi dalam kasus saya textview adalah subview dari beberapa tampilan kustom dan menambahkannya ke ViewController. Tidak ada solusi yang berhasil untuk saya. Untuk mengatasi masalah menambahkan penundaan 0,1 detik dan kemudian mengaktifkan gulir. Itu berhasil untuk saya.

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

Bagi saya, kode ini berfungsi dengan baik:

    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

Untuk menggulir ke posisi yang tepat dan menampilkannya di atas layar saya menggunakan kode ini:

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

Mengatur contentOffset.y menggulir ke akhir teks, lalu scrollRangeToVisible menggulir ke atas ke nilai scrollToLocation. Dengan demikian, posisi yang dibutuhkan muncul di baris pertama scrollView.


1

Bagi saya di iOS 9 itu berhenti bekerja untuk saya dengan string yang dikaitkan dengan metode scrollRangeToVisible (baris 1 dengan font tebal, baris lain dengan font biasa)

Jadi saya harus menggunakan:

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

dimana penundaan adalah:

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

Coba gunakan solusi 2 baris ini:

view.layoutIfNeeded()
textView.contentOffset = CGPointMake(textView.contentInset.left, textView.contentInset.top)

Ini berhasil untuk saya. UITextView saya berada di dalam UICollectionVewCell. Saya menduga bahwa scrollRangeToVisible menggulir ke tempat yang salah kecuali tata letak dihitung dengan benar
John Fowler

1

Ini kode saya. Ini bekerja dengan baik.

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()
      }
  }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.