web-dev-qa-db-fra.com

UITextView dans iOS7 coupe la dernière ligne de chaîne de texte

UITextView dans iOS7 a été vraiment bizarre. Lorsque vous tapez et entrez la dernière ligne de votre UITextView, la vue de défilement ne défile pas vers le bas comme elle le devrait et elle provoque le "découpage" du texte. J'ai essayé de définir sa propriété clipsToBound sur NO, mais elle coupe toujours le texte.

Je ne veux pas appeler "setContentOffset: animated" parce que pour un: c'est une solution très hacky .. deuxièmement: si le curseur était au milieu (verticalement) de notre affichage de texte, cela provoquera un défilement indésirable.

Voici une capture d'écran.

enter image description here

Toute aide serait grandement appréciée!

Merci!

54
ryan

Le problème est dû à iOS 7. Dans le délégué de la vue texte, ajoutez ce code:

- (void)textViewDidChange:(UITextView *)textView {
    CGRect line = [textView caretRectForPosition:
        textView.selectedTextRange.start];
    CGFloat overflow = line.Origin.y + line.size.height
        - ( textView.contentOffset.y + textView.bounds.size.height
        - textView.contentInset.bottom - textView.contentInset.top );
    if ( overflow > 0 ) {
        // We are at the bottom of the visible text and introduced a line feed, scroll down (iOS 7 does not do it)
        // Scroll caret to visible area
        CGPoint offset = textView.contentOffset;
        offset.y += overflow + 7; // leave 7 pixels margin
        // Cannot animate with setContentOffset:animated: or caret will not appear
        [UIView animateWithDuration:.2 animations:^{
            [textView setContentOffset:offset];
        }];
    }
}
93
davidisdk

La solution que j'ai trouvée ici était d'ajouter un correctif d'une ligne après avoir créé un UITextView:

self.textview.layoutManager.allowsNonContiguousLayout = NO;

Cette ligne fixe trois problèmes J'ai dû créer un éditeur de code basé sur UITextView avec mise en évidence de la syntaxe sur iOS7:

  1. Défilement pour garder le texte en vue lors de la modification (le problème de cet article)
  2. UITextView saute parfois après avoir fermé le clavier
  3. UITextView sauts de défilement aléatoire lorsque vous essayez de faire défiler la vue

Remarque, j'ai redimensionné l'ensemble de UITextView lorsque le clavier est affiché/masqué.

21
gmyers

Essayez d'implémenter le -textViewDidChangeSelection: méthode déléguée de UITextViewDelegate comme ceci:

-(void)textViewDidChangeSelection:(UITextView *)textView {
    [textView scrollRangeToVisible:textView.selectedRange];
}
6
chris

Voici une version modifiée de la réponse sélectionnée par davidisdk.

- (void)textViewDidChange:(UITextView *)textView {
    NSRange selection = textView.selectedRange;

    if (selection.location + selection.length == [textView.text length]) {
        CGRect caretRect = [textView caretRectForPosition:textView.selectedTextRange.start];
        CGFloat overflow = caretRect.Origin.y + caretRect.size.height - (textView.contentOffset.y + textView.bounds.size.height - textView.contentInset.bottom - textView.contentInset.top);

        if (overflow > 0.0f) {
            CGPoint offset = textView.contentOffset;
            offset.y += overflow + 7.0f;

            [UIView animateWithDuration:0.2f animations:^{
                [textView setContentOffset:offset];
            }];
        }
    } else {
        [textView scrollRangeToVisible:selection];
    }
}

J'obtenais un bogue selon lequel lorsque la taille du contenu de textView est plus grande que les limites et que le curseur est hors écran (comme utiliser un clavier et appuyer sur la touche fléchée), textView ne s'animerait pas avec le texte inséré.

2
cnotethegr8

À mon humble avis, c'est la réponse définitive à tous les problèmes typiques liés au défilement/au clavier UITextView dans iOS 7. C'est propre, facile à lire, facile à utiliser, facile à entretenir et peut être facilement réutilisé.

L'astuce de base: Modifiez simplement la taille de l'UITextView, pas l'encart de contenu!

Voici un exemple pratique. Il est évident que vous disposez d'un UIViewController basé sur NIB/Storyboard utilisant Autolayout et UITextView remplit l'intégralité de la vue racine dans UIViewController. Sinon, vous devrez adapter la façon dont vous modifiez textViewBottomSpaceConstraint à vos besoins.

Comment:


Ajoutez ces propriétés:

@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewBottomSpaceConstraint;
@property (nonatomic) CGFloat textViewBottomSpaceConstraintFromNIB;

Connectez le textViewBottomSpaceConstraint dans Interface Builder ( n'oubliez pas! )

Puis dans viewDidLoad:

// Save the state of the UITextView's bottom constraint as set up in your NIB/Storyboard
self.textViewBottomSpaceConstraintFromNIB = self.textViewBottomSpaceConstraint.constant;

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShowNotification:)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHideNotification:)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];

Ajoutez ces méthodes pour gérer le redimensionnement du clavier (grâce à https://github.com/brennanMKE/Interfaces/tree/master/Keyboarding - ces méthodes sont de brennan!):

- (void)keyboardWillShowNotification:(NSNotification *)notification {
    CGFloat height = [self getKeyboardHeight:notification forBeginning:TRUE];
    NSTimeInterval duration = [self getDuration:notification];
    UIViewAnimationOptions curve = [self getAnimationCurve:notification];

    [self keyboardWillShowWithHeight:height duration:duration curve:curve];
}

- (void)keyboardWillHideNotification:(NSNotification *)notification {
    CGFloat height = [self getKeyboardHeight:notification forBeginning:FALSE];
    NSTimeInterval duration = [self getDuration:notification];
    UIViewAnimationOptions curve = [self getAnimationCurve:notification];

    [self keyboardWillHideWithHeight:height duration:duration curve:curve];
}

- (NSTimeInterval)getDuration:(NSNotification *)notification {
    NSDictionary *info = [notification userInfo];

    NSTimeInterval duration;

    NSValue *durationValue = [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
    [durationValue getValue:&duration];

    return duration;
}

- (CGFloat)getKeyboardHeight:(NSNotification *)notification forBeginning:(BOOL)forBeginning {
    NSDictionary *info = [notification userInfo];

    CGFloat keyboardHeight;

    NSValue *boundsValue = nil;
    if (forBeginning) {
        boundsValue = [info valueForKey:UIKeyboardFrameBeginUserInfoKey];
    }
    else {
        boundsValue = [info valueForKey:UIKeyboardFrameEndUserInfoKey];
    }

    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    if (UIDeviceOrientationIsLandscape(orientation)) {
        keyboardHeight = [boundsValue CGRectValue].size.width;
    }
    else {
        keyboardHeight = [boundsValue CGRectValue].size.height;
    }

    return keyboardHeight;
}

- (UIViewAnimationOptions)getAnimationCurve:(NSNotification *)notification {
    UIViewAnimationCurve curve = [[notification.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];

    switch (curve) {
        case UIViewAnimationCurveEaseInOut:
            return UIViewAnimationOptionCurveEaseInOut;
            break;
        case UIViewAnimationCurveEaseIn:
            return UIViewAnimationOptionCurveEaseIn;
            break;
        case UIViewAnimationCurveEaseOut:
            return UIViewAnimationOptionCurveEaseOut;
            break;
        case UIViewAnimationCurveLinear:
            return UIViewAnimationOptionCurveLinear;
            break;
    }

    return kNilOptions;
}

Enfin, ajoutez ces méthodes pour réagir aux notifications du clavier et redimensionnez l'UITextView

- (void)keyboardWillShowWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
    CGFloat correctionMargin = 15; // you can experiment with this margin so the bottom text view line is not flush against the keyboard which doesn't look Nice
    self.textViewBottomSpaceConstraint.constant = height + correctionMargin;

    [self.view setNeedsUpdateConstraints];

    [UIView animateWithDuration:duration delay:0 options:curve animations:^{
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {

    }];
}

- (void)keyboardWillHideWithHeight:(CGFloat)height duration:(CGFloat)duration curve:(UIViewAnimationOptions)curve
{
    self.textViewBottomSpaceConstraint.constant = self.textViewBottomSpaceConstraintFromNIB;

    [self.view setNeedsUpdateConstraints];

    [UIView animateWithDuration:duration delay:0 options:curve animations:^{
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {

    }];
}

Ajoutez également ces méthodes pour faire défiler automatiquement où l'utilisateur a cliqué

- (void)textViewDidBeginEditing:(UITextView *)textView
{
    [textView scrollRangeToVisible:textView.selectedRange];
}

- (void)textViewDidChangeSelection:(UITextView *)textView
{
    [textView scrollRangeToVisible:textView.selectedRange];
}
1
Wirsing

Si vous utilisez StoryBoard, ce comportement peut également se produire si vous avez laissé AutoLayout activé (comme c'est le cas par défaut) et n'avez pas défini de contraintes haut/bas pour votre UITextView. Vérifiez l'inspecteur de fichiers pour voir votre statut de mise en page automatique ...

0
Fabrice

Voici la version MonoTouch de la meilleure solution de davididsk (vue d'en haut).

TextView.SelectionChanged += (object sender, EventArgs e) => {
                TextView.ScrollRangeToVisible(TextView.SelectedRange);
            };


            TextView.Changed += (object sender, EventArgs e) => {

                CGRect line = TextView.GetCaretRectForPosition(TextView.SelectedTextRange.Start);
                nfloat overflow = line.Y + line.Height - 
                                     (TextView.ContentOffset.Y + 
                                      TextView.Bounds.Height -          
                                      TextView.ContentInset.Bottom -
                                      TextView.ContentInset.Top );
                if ( overflow > 0 ) 
                {
                    // We are at the bottom of the visible text and introduced 
                    // a line feed, scroll down (iOS 7 does not do it)
                    // Scroll caret to visible area
                    CGPoint offset = TextView.ContentOffset;
                    offset.Y+= overflow + 7; // leave 7 pixels margin
                    // Cannot animate with setContentOffset:animated: 
                    // or caret will not appear
                    UIView.Animate(0.1,()=> {
                        TextView.ContentOffset = offset;
                    });
                }
            };
0
aumansoftware

Cette ligne empêche la dernière ligne de texte de s'afficher pour moi:

textView.scrollEnabled = false

Essayez de supprimer cela et voyez ce qui se passe ...

0
noobular
textView.contentInset = UIEdgeInsetsMake(0.0, 0.0, 10.0, 0.0);

Cela permettra également de résoudre votre problème

0
Dimitris