OK, j'ai un problème avec le UITextView
. Voici le problème:
J'ajoute du texte à un UITextView
. L'utilisateur double-clique ensuite pour sélectionner quelque chose. Je change ensuite le texte dans le UITextView
(par programmation comme ci-dessus) et le UITextView
défile vers le bas de la page où il y a un curseur.
Cependant, ce n'est PAS là où l'utilisateur a cliqué. Il défile TOUJOURS vers le bas du UITextView
quel que soit l'endroit où l'utilisateur a cliqué.
Voici donc ma question: comment forcer le UITextView
à défiler vers le haut chaque fois que je change le texte? J'ai essayé contentOffset
et scrollRangeToVisible
. Aucun travail.
Toute suggestion serait appréciée.
UITextView*note;
[note setContentOffset:CGPointZero animated:YES];
Ça le fait pour moi.
Version Swift (Swift 4.1 avec iOS 11 sur Xcode 9.3):
note.setContentOffset(.zero, animated: true)
Si quelqu'un a ce problème dans iOS 8, j'ai trouvé que le simple réglage du UITextView's
propriété de texte, puis appelez scrollRangeToVisible
avec un NSRange
avec location:0
, length:0
, travaillé. Mon affichage de texte n'était pas modifiable et j'ai testé à la fois sélectionnable et non sélectionnable (aucun paramètre n'a affecté le résultat). Voici un exemple Swift:
myTextView.text = "Text that is long enough to scroll"
myTextView.scrollRangeToVisible(NSRange(location:0, length:0))
Et voici ma solution ...
override func viewDidLoad() {
textView.scrollEnabled = false
textView.text = "your text"
}
override func viewDidAppear(animated: Bool) {
textView.scrollEnabled = true
}
Appel
scrollRangeToVisible(NSMakeRange(0, 0))
fonctionne mais appelez-le
override func viewDidAppear(animated: Bool)
J'utilisais attributeString en HTML avec une vue texte non modifiable. La définition du décalage de contenu n'a pas fonctionné pour moi non plus. Cela a fonctionné pour moi: désactiver le défilement activé, définir le texte, puis réactiver le défilement
[yourTextView setScrollEnabled:NO];
yourTextView.text = yourtext;
[yourTextView setScrollEnabled:YES];
Puisqu'aucune de ces solutions n'a fonctionné pour moi et que j'ai perdu trop de temps à rassembler des solutions, cela a finalement résolu le problème pour moi.
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)
})
}
C'est vraiment stupide que ce ne soit pas un comportement par défaut pour ce contrôle.
J'espère que cela aide quelqu'un d'autre.
Je ne sais pas si je comprends votre question, mais essayez-vous simplement de faire défiler la vue vers le haut? Si oui, vous devriez le faire
[textview scrollRectToVisible:CGRectMake(0,0,1,1) animated:YES];
Pour iOS 10 et Swift 3 j'ai fait:
override func viewDidLoad() {
super.viewDidLoad()
textview.isScrollEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.isScrollEnabled = true
}
A fonctionné pour moi, je ne sais pas si vous rencontrez le problème avec la dernière version de Swift et iOS.
J'ai une réponse mise à jour qui affichera correctement la vue de texte et sans que l'utilisateur ne subisse d'animation de défilement.
override func viewWillAppear(animated: Bool) {
dispatch_async(dispatch_get_main_queue(), {
self.introductionText.scrollRangeToVisible(NSMakeRange(0, 0))
})
Voilà comment je l'ai fait
Swift 4:
extension UITextView {
override open func draw(_ rect: CGRect)
{
super.draw(rect)
setContentOffset(CGPoint.zero, animated: false)
}
}
Voici comment cela a fonctionné sur la version iOS 9 afin que le texteView défile en haut avant d'apparaître à l'écran
- (void)viewDidLoad
{
[super viewDidLoad];
textView.scrollEnabled = NO;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
textView.scrollEnabled = YES;
}
Mon problème est de définir textView pour faire défiler vers le haut lorsque la vue est apparue. Pour iOS 10.3.2 et Swift 3.
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)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// It works.
self.textView.contentOffset = CGPoint.zero
}
[txtView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
Cette ligne de code fonctionne pour moi.
Combiné les réponses précédentes, maintenant cela devrait fonctionner:
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
Essayez ceci pour déplacer le curseur en haut du texte.
NSRange r = {0,0};
[yourTextView setSelectedRange:r];
Voyez comment ça se passe. Assurez-vous d'appeler cela après que tous vos événements se soient déclenchés ou que ce que vous faites soit terminé.
Swift 2 Réponse:
textView.scrollEnabled = false
/* Set the content of your textView here */
textView.scrollEnabled = true
Cela empêche le textView de défiler jusqu'à la fin du texte après l'avoir défini.
Dans Swift 3:
viewWillLayoutSubviews est l'endroit pour effectuer des modifications. viewWillAppear devrait également fonctionner, mais logiquement, layout devrait être exécuté dans viewWillLayoutSubviews.
Concernant les méthodes, scrollRangeToVisible et setContentOffset fonctionneront. Cependant, setContentOffset permet à l'animation d'être désactivée ou activée.
Supposons que ITextView est nommé yourUITextView. Voici le code:
// 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)
}
Cela a fonctionné pour moi
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
self.textView.scrollRangeToVisible(NSRange(location: 0, length: 0))
}
}
-(void) viewDidAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
- (void)viewWillAppear:(BOOL)animated{
[mytextView scrollRangeToVisible:NSMakeRange(0,0)];}
ViewWillappear
le fera instantanément avant que l'utilisateur ne le remarque, mais la section ViewDidDisappear
l'animera paresseusement.[mytextView setContentOffset:CGPointZero animated:YES];
ne fonctionne PAS parfois (je ne sais pas pourquoi), utilisez plutôt scrollrangetovisible.Cela a fonctionné pour moi. Il est basé sur la réponse de RyanTCB mais c'est la variante Objective-C de la même solution:
- (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)];
}
vous pouvez juste utiliser ce code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textView.scrollRangeToVisible(NSMakeRange(0, 0))
}
Problème résolu: iOS = 10.2 Swift 3 UITextView
Je viens d'utiliser la ligne suivante:
displayText.contentOffset.y = 0
J'ai eu le problème, mais dans mon cas, textview est la sous-vue d'une vue personnalisée et l'a ajoutée à ViewController. Aucune solution n'a fonctionné pour moi. Pour résoudre le problème, ajoutez un délai de 0,1 seconde, puis activez le défilement. Ça a marché pour moi.
textView.isScrollEnabled = false
..
textView.text = // set your text here
let dispatchTime = DispatchTime.now() + 0.1
DispatchQueue.main.asyncAfter(deadline: dispatchTime) {
self.textView.isScrollEnabled = true
}
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
[textview setContentOffset:CGPointZero animated:NO];
}
[self.textView scrollRectToVisible:CGRectMake(0, 0, 320, self.textView.frame.size.height) animated:NO];
Pour moi, ça fonctionne bien ce code:
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
Pour faire défiler jusqu'à la position exacte et l'afficher en haut de l'écran, j'utilise ce code:
var scrollToLocation = 50 //<needed position>
textView.contentOffset.y = textView.contentSize.height
textView.scrollRangeToVisible(NSRange.init(location: scrollToLocation, length: 1))
La définition de contentOffset.y défile jusqu'à la fin du texte, puis scrollRangeToVisible défile jusqu'à la valeur de scrollToLocation. Ainsi, la position requise apparaît dans la première ligne de scrollView.
Essayez d'utiliser cette solution à 2 lignes:
view.layoutIfNeeded()
textView.contentOffset = CGPointMake(textView.contentInset.left, textView.contentInset.top)
Je pense que la raison pour laquelle certaines personnes rencontrent des problèmes avec ce problème est qu'elles essaient de définir la position de défilement TextView
, avant que ce TextView
n'apparaisse.
Il n'y a rien à faire défiler, quand le TextView
n'est pas encore apparu.
Vous devez attendre que TextView
apparaisse en premier, puis seulement définir sa position de défilement dans la méthode viewDidAppear
comme indiqué ci-dessous:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.setContentOffset(CGPoint.zero, animated: false)
}
Voici mes codes. Ça fonctionne bien.
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()
}
}
Ce code résout le problème et permet également d'éviter le défilement automatique inutile jusqu'à la fin de textView. Utilisez simplement la méthode setTextPreservingContentOffset:
au lieu de définir directement le texte sur textView.
@interface ViewController () {
__weak IBOutlet UITextView *textView;
CGPoint lastContentOffset;
NSTimer *autoscrollTimer;
BOOL revertAnyContentOffsetChanges;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[textView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)dealloc
{
[textView removeObserver:self forKeyPath:@"contentOffset"];
}
- (void)setTextPreservingContentOffset:(NSString *)text
{
lastContentOffset = textView.contentOffset;
[autoscrollTimer invalidate];
revertAnyContentOffsetChanges = YES;
textView.text = text;
autoscrollTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(enableScrolling:) userInfo:nil repeats:NO];
autoscrollTimer.tolerance = 1;
}
- (void)enableScrolling:(NSTimer *)timer
{
revertAnyContentOffsetChanges = NO;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (revertAnyContentOffsetChanges && [keyPath isEqualToString:@"contentOffset"] && [[change valueForKey:@"new"] CGPointValue].y != lastContentOffset.y) {
[textView setContentOffset:lastContentOffset animated:NO];
}
}
@end
J'ai dû appeler ce qui suit à l'intérieur viewDidLayoutSubviews (appeler à l'intérieur de viewDidLoad était trop tôt):
myTextView.setContentOffset(.zero, animated: true)
Pour moi dans iOS 9, cela a cessé de fonctionner pour moi avec la chaîne attribuée avec la méthode scrollRangeToVisible (la 1 ligne était en police en gras, les autres lignes étaient en police normale)
J'ai donc dû utiliser:
textViewMain.attributedText = attributedString
textViewMain.scrollRangeToVisible(NSRange(location:0, length:0))
delay(0.0, closure: {
self.textViewMain.scrollRectToVisible(CGRectMake(0, 0, 10, 10), animated: false)
})
où le retard est:
func delay(delay:Double, closure:()->Void) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
La version Swift 3 de la réponse de drew_s plus tôt, qui est la seule chose qui a fonctionné pour moi après avoir essayé la plupart des autres réponses.
override func viewWillAppear(_ animated: Bool) {
DispatchQueue.main.async{
let desiredOffset = CGPoint(x: 0, y: -self.person_description.contentInset.top)
self.person_description.setContentOffset(desiredOffset, animated: false)
}
}
Il s'agissait d'un copier-coller de code de travail. Remplacez person_description par votre variable textview.
La réponse de ChallengerGuy a complètement résolu mon problème une fois que j'ai ajouté un bref délai avant de réactiver le défilement. Avant d'ajouter le délai, mes pages PageController étaient toutes fixes, à l'exception de la première page, qui se corrigeait également après toute interaction de l'utilisateur avec la page. L'ajout du délai a également corrigé la position de défilement UITextView de cette première page.
override open func viewDidLoad() {
super.viewDidLoad()
textView.isScrollEnabled = false
textView.text = textToScroll
... other stuff
}
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Central().delay(0.5) {
self.textView.isScrollEnabled = true
}
}
(La fonction de retard dans mon fichier Central ().)
func delay(_ delay: Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
closure()
}
}
Pour Swift je pense que la façon la plus propre de le faire est de sous-classer UITextView et d'utiliser cette petite astuce
import UIKit
class MyTextView: UITextView {
override var text: String! {
willSet {
isScrollEnabled = false
} didSet {
isScrollEnabled = true
}
}
}