Metni her değiştirdiğimde bir UITextView’u en üste kaydırmaya nasıl zorlarım?


100

Tamam, ile bazı problemler yaşıyorum UITextView. Sorun şu:

Bir UITextView. Kullanıcı daha sonra bir şey seçmek için çift tıklar. Daha sonra UITextView(yukarıdaki gibi programlı olarak) içindeki metni değiştiririm ve UITextView bir imlecin bulunduğu sayfanın altına kaydırırım.

Ancak, kullanıcının tıkladığı yer bu DEĞİLDİR. DAİMA UITextViewkullanıcının tıkladığı yere bakılmaksızın en altına kayar .

Öyleyse işte sorum şu: UITextViewMetni her değiştirdiğimde en üste kaydırmaya nasıl zorlarım? Denedim contentOffsetve scrollRangeToVisible. Hiçbiri çalışmıyor.

Herhangi bir öneri memnuniyetle karşılanacaktır.

Yanıtlar:


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

Bu benim için yapıyor.

Swift sürümü (Xcode 9.3'te iOS 11 ile Swift 4.1):

note.setContentOffset(.zero, animated: true)

13
Bana yardım etme.
Dmitry

6
Bu işe yarıyor, ama yine de bu neden gerekli? Apple'ın kaydırma mantığının iyileştirilmesi gerekiyor, işlerin çoğu durumda otomatik olarak olması gerektiği gibi davranmasını sağlamak için çok fazla çember atlama var.
John Contarino

4
Benim için iOS 9'da çalışıyor, ancak daha önce değil viewDidAppear().
Murray Sagal

4
Sen () viewDidLayoutSubviews bunu arayarak bunu daha önce viewDidAppear () daha çağırabilir
arcady bob

Tablonun hücresine metin görünümü ekledim, bu yüzden bunu satır için hücrede ayarlıyorum ama çalışmıyorum
Chandni

67

Herkes iOS 8'de bu sorun varsa, bunu sadece ayar buldum UITextView'ssonra arayarak, metin özelliği scrollRangeToVisiblebir ile NSRangebirlikte location:0, length:0çalıştı. Metin görünümüm düzenlenebilir değildi ve hem seçilebilir hem de seçilemez olarak test ettim (hiçbir ayar sonucu etkilemedi). İşte bir Swift örneği:

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

teşekkürler, yukarıdakilerin hiçbiri benim için işe yaramadı. Örneğiniz yaptı.
user1244109

Bu işe yarıyor, ancak devre dışı bırakılması gereken bir kaydırma animasyonu alıyoruz, kaydırmayı devre dışı bırakmayı ve @miguel'in önerdiği gibi yeniden etkinleştirmeyi tercih ediyorum
Warpzit

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

Bu artık işe yaramıyor gibi görünüyor. iOS 9 ve Xcode 7.3.1: /
wasimsandhu

Swift 3 Stll çalışıyor! :) sadece bu satırı UITextFielDelegate yönteminize kopyalayın. `func textViewDidEndEditing (_ textView: UITextView) {textView.scrollRangeToVisible (NSRange (konum: 0, uzunluk: 0))}` `
lifewithelliott

46

Ve işte benim çözümüm ...

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

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

5
Bu çözümü sevmiyorum ama en azından iOS 9 beta 5 itibariyle benim için çalışan tek şey bu. Diğer çözümlerin çoğunun metin görünümünüzün zaten görünür olduğunu varsaydığını varsayıyorum. Benim durumumda bu her zaman doğru değil, bu yüzden görünene kadar kaydırmayı devre dışı bırakmalıyım.
robotspacer

1
Benim için de işe yarayan tek şey buydu. Düzenlenemez bir metin görünümü kullanan iOS 9.
SeanR

1
Bu benim için işe yarıyor, iOS 8, ilişkilendirilmiş dizeyle düzenlenemez metin görünümü. In viewWillAppear - çalışmıyor
Tina Zh

@Robotspacer'a katılıyorum. ve bu çözüm hala benim için çalışan tek çözüm.
lerp90

Benim için bu çözüm yalnızca kısa (atıfta bulunulan) metinlerle çalıştı. Daha uzun metinler olduğunda, UITextView kaydırma hatası canlı kalmaya devam ediyor :-) Benim (biraz çirkin) çözümüm, kaydırmayı yeniden etkinleştirmek için 2 saniye gecikmeyle dispatch_after kullanmaktı. UITextView, metin düzenini yapmak için biraz zamana ihtiyaç duyuyor gibi görünüyor ...
LaborEtArs

38

Aranıyor

scrollRangeToVisible(NSMakeRange(0, 0))

çalışıyor ama ara

override func viewDidAppear(animated: Bool)

4
ViewDidAppear'da yalnızca benim için çalıştığını onaylıyorum.
eric f.

2
Bu benim için çalışan tek kişi. Teşekkürler. Ama biraz can sıkıcı bulduğum animasyonu nasıl devre dışı bırakacaksınız?
rockhammer

Bu işe yarasa da, I @Maria'nın çözümü daha iyi, çünkü bu kısa bir "atlama" göstermiyor.
Sipke Schoorstra

1
Son yorumumu geri alıyorum; hem iOS 10 hem de iOS 11'de çalıştığı için bu benim için en iyi yanıt, oysa @ Maria'nın yanıtı iOS 11'de
pek işe yaramıyor.

@SipkeSchoorstra, bkz cevabım o (Kvo kullanarak) herhangi atlar olmadan çalışması için aşağıda.
Terry

29

Metin görünümü düzenlenemezken HTML'de attributedString kullanıyordum. İçerik ofsetini ayarlamak benim için de işe yaramadı. Bu benim için çalıştı: kaydırmayı devre dışı bırakın, metni ayarlayın ve ardından kaydırmayı tekrar etkinleştirin

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

Bir tableViewCell içinde bir textView vardı. Bu durumda textView yakl. % 50. Bu çözüm mantıklı olduğu kadar basittir. Teşekkürler
Julian F.Weinert

1
@ JulianF.Weinert Bu, iOS10 için çalışmıyor gibi görünüyor. Bir tableViewCell'deki textView ile sizinle aynı duruma sahibim. TextView’un düzenleme özelliğini kullanmıyorum. Sadece kaydırılabilir metin istiyorum. Ancak textView, söylediğiniz gibi her zaman yaklaşık% 50'ye kaydırılır. Bu yazıda setContentOffset ve scrollRangeToVisible'ı ayarlayarak diğer tüm önerileri de denedim. Hiçbiri çalışmıyor. Bunun işe yaraması için yapmış olabileceğiniz başka bir şey var mı?
JeffB6688

@ JeffB6688 üzgünüm, hayır. Bu gerçekten uzun zaman önceydi ... "Yeni" bir iOS sürümünün başka bir tuhaflığı olabilir mi?
Julian F. Weinert

Ancak maalesef iOS 11.2'de mükemmel değil. Onlu @RyanTCB'nin yanıtı hem iOS 10 hem de iOS 11'de çalışıyor.
Sipke Schoorstra

Bu benim için şu ana kadar iOS 9, 10 ve 11'de iyi çalışıyor, ancak viewDidAppeardaha geniş kapsamlı düzeltmelerin bir parçası olarak bunu etkinleştirmek zorunda kaldım
MadProgrammer

19

Bu çözümlerden hiçbiri benim için işe yaramadığından ve çözümleri bir araya getirmek için çok fazla zaman harcadığım için, bu sonunda benim için çözdü.

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

Bu, bu kontrol için varsayılan davranış olmaması gerçekten aptalca.

Umarım bu bir başkasına yardım eder.


2
Bu çok yardımcı oldu! Ayrıca şunu eklemem gerekiyordu: self.automaticallyAdjustsScrollViewInsets = NO; tam olarak çalışmasını sağlamak için UITextView içeren görünüme.
jengelsma

self.textView.contentOffset.yBu cevabı temel alan başka bir hesaplama hesaplamasıyla yalnızca SWIFT 3'te çalışmasını
sağladım

Swift 3
hawmack13

Bunun ne kadar çılgınca olduğuna tamamen katılıyor musunuz? çözüm için teşekkürler
ajonno

18

Sorunuzu anladığımdan emin değilim, ancak görünümü yukarı kaydırmaya mı çalışıyorsunuz? Eğer öyleyse yapmalısın

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


1
@SamStewart Bağlantı bozuk görünüyor, her zaman hesaplarım sayfasına yönlendiriyor (zaten oturum açmış) orada hangi çözümü sundukları hakkında daha fazla bilgi verebilir misiniz?
uliwitness

@uliwitness Bu artık çok eski bir bilgi, 8 yıllık bir gönderi yerine daha modern bir kaynağı denemenizi tavsiye ederim. İOS API, bu 8 yılda önemli ölçüde değişti.
Benjamin Sussman

1
Daha yeni bilgiler bulmaya çalıştım. Nerede bulabileceğimi biliyorsanız, minnettar olurum. Hala aynı sorun gibi görünüyor :(
uliwitness

Teşekkürler, bu benim için çalıştı çünkü Kullanıcı Tanımlı Çalışma Zamanı ÖzellikleriTextView.scrollRectToVisible(CGRect(x: 0,y: 0,width: 1,height: 1), animated: false)
Kurt Lane

13

İOS 10 ve Swift 3 için şunları yaptım:

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

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

Benim için çalıştı, Swift ve iOS'un en son sürümüyle ilgili sorun yaşayıp yaşamadığından emin değilim.


Takip etmek için mükemmel bir çözüm!
iChirag

Bir UIPageViewController tarafından kontrol edilen bir UIView'de bir UITextView ile kaydırma sorunu yaşıyorum. (Bu sayfalar Yardım / Hakkında ekranlarımı gösterir.) Çözümünüz, ilki hariç her sayfada çalışır. Herhangi bir sayfaya hızlıca kaydırmak ve ilk sayfaya geri dönmek, o ilk sayfanın kaydırma konumunu düzeltir. ViewWillAppear'a başka bir .isScrollEnabled = False eklemeyi denedim, ancak bunun bir etkisi olmadı. İlk sayfanın neden diğerlerinden farklı davrandığı konusunda şaşkınım.
Wayne Henderson

[güncelleme] Az önce çözdüm! .İsScrollEnabled = true tetikleyicilerinden önce 0,5 saniyelik bir gecikme getirilmesi, ilk yükleme sorununu çözer.
Wayne Henderson

çok hoş . . .
Maysam R

Bir cazibe gibi çalıştı. Benim için çalışan tek çözüm. Adamsın.
Lazy Ninja

10

Metin görünümünün düzgün şekilde görünmesini sağlayacak ve kullanıcı bir kaydırma animasyonu yaşamadan güncellenmiş bir cevabım var.

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

Neden ana kuyrukta yapıyorsunuz, sinüs görünümüWillAppear ana kuyrukta çalışıyor?
karthikPrabhu Alagu

1
Benim için önce çok fazla uyduları hatırlamak ama onun burada ele: stackoverflow.com/a/17490329/4750649
MacInnis

8

TextView ekranda görünmeden önce en üste kaydırıldığı için iOS 9 Release'de böyle çalıştı.

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

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

1
Bu hile yaptı. Bu arada, bana çok önemli bir şey öğretti: 'görünüm görünecek''teki' görüş 'sadece kendi bakış açısına değil, görünüşe göre TÜM görüşlere atıfta bulunuyor. Hayır?
Sjakelien

8

Ben böyle yaptım

Swift 4:

extension UITextView {

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

 }

Benim için en iyi sonuçları veren tek yöntem bu. Oy veriyorum.
smoothBlue

7

Bu benim için çalıştı

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

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

Bu çok tuhaf. Çoğu cihazda aramayı sarmak DispatchQueue.main.async(...gerekli değildir, ancak bazı nedenlerden dolayı (örneğin) iPhone 5s'de (iOS 12.1) siz göndermedikçe çalışmaz. Ancak hata ayıklayıcıdaki çağrı yığını tarafından bildirildiği gibi viewWillAppear() , zaten her durumda ana kuyrukta çalışıyor ... - WTF, Apple ???
Nicolas Miari

2
Ayrıca ana kuyruktaki tüm olayları çalıştırıyor olmama rağmen neden ana kuyrukta açıkça göndermem gerektiğini merak ediyordum. Ama bu benim için çalıştı
Ankit garg

6

İmleci metnin en üstüne taşımak için bunu deneyin.

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

Nasıl gittiğini görün. Tüm olaylarınız tetiklendikten veya yaptığınız her şey bittikten sonra bunu çağırdığınızdan emin olun.


denedim adamım, şans yok. FirstResponder ile bir şey gibi görünüyor. Başka öneriniz var mı?
Sam Stewart

imleç konumu ile aynı sorun var, benim için çalıştı ... bunun için harika +1.
Dhaval

aşağıdan yukarıya animasyon alıyorum ama buna gerek duymadım, bu yüzden onu nasıl kaldıracağım.
Dhaval

2
En üste kaymaz (birkaç pikselde daha az).
Dmitry

6

Benim sorunum textView'ı görünüm göründüğünde en üste kaydıracak şekilde ayarlamak. İOS 10.3.2 ve Swift 3 için.

1.Çözüm:

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.Çözüm:

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

5

Önceki cevaplar birleştirildiğinde, şimdi işe yaramalı:

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

Benim için işe yarayan tek şey bu ne kadar aptalca. scrollRangeToVisibleYine de kaldırabildim .
Jordan H

NSRange (0,0) 'ı NSMakeRange (0,0) ile değiştirmeniz gerekecek
RobP

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

Bu kod satırı benim için çalışıyor.


5

sadece bu kodu kullanabilirsin

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

4

Hızlı 2 Cevap:

textView.scrollEnabled = false

/* Set the content of your textView here */

textView.scrollEnabled = true

Bu, textView'ın ayarlandıktan sonra metnin sonuna kaymasını engeller.


4

Swift 3'te:

  • viewWillLayoutSubviews , değişikliklerin yapılacağı yerdir. viewWillAppear da çalışmalıdır, ancak mantıksal olarak düzen , viewWillLayoutSubviews'te gerçekleştirilmelidir .

  • Yöntemlerle ilgili olarak, hem scrollRangetoVisible ve setContentOffset çalışacaktır. Ancak setContentOffset , animasyonun kapalı veya açık olmasına izin verir.

UITextView’un sizinUITextView olarak adlandırıldığını varsayın . İşte kod:

// 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, herhangi bir görünüm yeniden boyutlandırmasında çağrılır. Genelde istediğin bu değil.
uliwitness

4

ViewDidLayoutSubviews içinde aşağıdaki çağrıyı çağırmak zorunda kaldım (viewDidLoad içinde arama çok erken oldu):

    myTextView.setContentOffset(.zero, animated: true)

3

Bu benim için çalıştı. RyanTCB'nin cevabına dayanmaktadır, ancak aynı çözümün Amaç-C çeşididir:

- (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, herhangi bir görünüm yeniden boyutlandırmasında çağrılır. Genelde istediğin bu değil.
uliwitness

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

- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
  • ViewWillappearkullanıcının farkına varmadan hemen yapacaktır, ancak ViewDidDisappearbölüm tembel bir şekilde onu canlandıracaktır.
  • [mytextView setContentOffset:CGPointZero animated:YES]; bazen ÇALIŞMAZ (nedenini bilmiyorum), bu yüzden bunun yerine scrollrangetovisible kullanın.

3

Sorun çözüldü: iOS = 10.2 Swift 3 UITextView

Sadece aşağıdaki satırı kullandım:

displayText.contentOffset.y = 0

1
Çalıştı (iOS 10.3)! İçine ekledim, viewDidLayoutSubviewsböylece ekran döndüğünde olur. Aksi takdirde, içerik ofseti yanlış hizalanır.
Pup

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

Lütfen daha fazla ayrıntıyla düzenleyin.
Şemetrik

2

Sorun yaşadım, ancak benim durumumda metin görünümü bazı özel görünümlerin alt görünümü ve ViewController'a ekledim. Çözümlerin hiçbiri benim için işe yaramadı. Sorunu çözmek için 0,1 saniye gecikme eklendi ve ardından kaydırmayı etkinleştirin. Benim için çalıştı.

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

Benim için bu kod iyi çalışıyor:

    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

Tam konuma kaydırmak ve bunu ekranın üstünde göstermek için şu kodu kullanıyorum:

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

ContentOffset.y ayarının yapılması metnin sonuna kaydırılır ve ardından scrollToVisible scrollToLocation değerine kadar kaydırır. Böylelikle gerekli konum scrollView'in ilk satırında görünür.


1

Benim için iOS 9'da, scrollRangeToVisible yöntemiyle ilişkilendirilmiş dizeyle benim için çalışmayı durdurdu (1 satır kalın yazı tipindeydi, diğer satırlar normal yazı tipindeydi)

Bu yüzden kullanmak zorunda kaldım:

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

gecikme nerede:

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

Bu 2 satırlık çözümü kullanmayı deneyin:

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

Bu benim için çalıştı. Benim UITextView bir UICollectionVewCell içindeydi. Düzeni doğru bir şekilde hesaplanmadıkça scrollRangeToVisible'ın yanlış noktaya kayacağını düşünüyorum
John Fowler

1

İşte kodlarım. İyi çalışıyor.

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()
      }
  }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.