web-dev-qa-db-fra.com

Centrer le texte verticalement dans une UITextView

Je souhaite centrer le texte verticalement dans une grande UITextView qui remplit tout l'écran. Ainsi, lorsqu'il y a peu de texte, prononcez quelques mots, il est centré par la hauteur. Ce n’est pas une question de centrage du texte (une propriété que l’on trouve dans IB) mais de mettre le texte verticalement en plein milieu de UITextViewsi le texte est court, pas de zones vierges dans la UITextView. Est-ce possible? Merci d'avance!

57
Sergey Grischyov

Premièrement, ajoutez un observateur pour la valeur de la clé contentSize de la UITextView lorsque la vue est chargée:

- (void) viewDidLoad {
     [textField addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
     [super viewDidLoad];
}

Ajoutez ensuite cette méthode pour ajuster la contentOffset chaque fois que la valeur contentSize change:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
     UITextView *tv = object;
     CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
     topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
     tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
59

Parce que UIKit n'est pas conforme à KVO , j'ai décidé de l'implémenter en tant que sous-classe de UITextView qui se met à jour chaque fois que la variable contentSize change. 

C'est une version légèrement modifiée de La réponse de Carlos qui définit contentInset au lieu de contentOffset. En plus de étant compatible avec iOS 9 , il semble également être moins bogué sur iOS 8.4.

class VerticallyCenteredTextView: UITextView {
    override var contentSize: CGSize {
        didSet {
            var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
            topCorrection = max(0, topCorrection)
            contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
        }
    }
}
44
Senseful

Si vous ne souhaitez pas utiliser KVO, vous pouvez également régler manuellement le décalage en exportant ce code vers une fonction telle que

-(void)adjustContentSize:(UITextView*)tv{
    CGFloat deadSpace = ([tv bounds].size.height - [tv contentSize].height);
    CGFloat inset = MAX(0, deadSpace/2.0);
    tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right);
}  

et en l'appelant 

-(void)textViewDidChange:(UITextView *)textView{
    [self adjustContentSize:textView];
}

et chaque fois que vous modifiez le texte dans le code. N'oubliez pas de définir le contrôleur en tant que délégué

Version Swift 3:  

func adjustContentSize(tv: UITextView){
    let deadSpace = tv.bounds.size.height - tv.contentSize.height
    let inset = max(0, deadSpace/2.0)
    tv.contentInset = UIEdgeInsetsMake(inset, tv.contentInset.left, inset, tv.contentInset.right)
}

func textViewDidChange(_ textView: UITextView) {
    self.adjustContentSize(tv: textView)
}
21
Beninho85

Pour iOS 9.0.2. nous aurons besoin de définir le contentInset à la place. Si nous KVO le contentOffset, iOS 9.0.2 le définit à 0 au dernier moment, annulant les modifications apportées à contentOffset.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    UITextView *tv = object;
    CGFloat topCorrect = ([tv bounds].size.height - [tv     contentSize].height * [tv zoomScale])/2.0;
    topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
    [tv setContentInset:UIEdgeInsetsMake(topCorrect,0,0,0)];
}

- (void) viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:NO];
    [questionTextView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
}

J'ai utilisé 0,0 et 0 pour les inserts Edge gauche, bas et droit respectivement. Assurez-vous de les calculer également pour votre cas d'utilisation. 

20
s.ka

Voici une extension UITextView qui centre le contenu verticalement:

extension UITextView {

    func centerVertically() {
        let fittingSize = CGSize(width: bounds.width, height: CGFloat.max)
        let size = sizeThatFits(fittingSize)
        let topOffset = (bounds.size.height - size.height * zoomScale) / 2
        let positiveTopOffset = max(0, topOffset)
        contentOffset.y = -positiveTopOffset
    }

}
6
Rudolf Adamkovič

Swift 3:

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        textField.frame = self.view.bounds

        var topCorrect : CGFloat = (self.view.frame.height / 2) - (textField.contentSize.height / 2)
        topCorrect = topCorrect < 0.0 ? 0.0 : topCorrect
        textField.contentInset = UIEdgeInsetsMake(topCorrect,0,0,0)

    }
5
user5195185
func alignTextVerticalInTextView(textView :UITextView) {

    let size = textView.sizeThatFits(CGSizeMake(CGRectGetWidth(textView.bounds), CGFloat(MAXFLOAT)))

    var topoffset = (textView.bounds.size.height - size.height * textView.zoomScale) / 2.0
    topoffset = topoffset < 0.0 ? 0.0 : topoffset

    textView.contentOffset = CGPointMake(0, -topoffset)
}
5
Ievgen

Vous pouvez le configurer directement avec seulement des contraintes:

Il y a 3 contraintes que j'ai ajoutées pour aligner le texte verticalement et horizontalement, comme ci-dessous:

 enter image description here

  1. Faites la hauteur 0 et ajoutez des contraintes supérieures à
  2. Ajouter un alignement vertical aux contraintes parent
  3. Ajouter un alignement horizontal aux contraintes parent

 enter image description here

5
KOTIOS

J'ai une vue texte que j'utilise avec autolayout et avec la définition de lineFragmentPadding et textContainerInset à zéro. Aucune des solutions ci-dessus n'a fonctionné dans ma situation. Cependant, cela fonctionne pour moi. Testé avec iOS 9

@interface VerticallyCenteredTextView : UITextView
@end

@implementation VerticallyCenteredTextView

-(void)layoutSubviews{
    [self recenter];
}

-(void)recenter{
    // using self.contentSize doesn't work correctly, have to calculate content size
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)];
    CGFloat topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0;
    self.contentOffset = CGPointMake(0, -topCorrection);
}

@end
5
user1687195

J'ai aussi ce problème et je l'ai résolu avec un UITableViewCell avec UITextView. J'ai créé la méthode dans une sous-classe UITableViewCell personnalisée, propriété statusTextView:

- (void)centerTextInTextView
{
    CGFloat topCorrect = ([self.statusTextView bounds].size.height - [self.statusTextView contentSize].height * [self.statusTextView zoomScale])/2.0;
    topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
    self.statusTextView.contentOffset = (CGPoint){ .x = 0, .y = -topCorrect };

Et appelez cette méthode en méthodes:

- (void)textViewDidBeginEditing:(UITextView *)textView
- (void)textViewDidEndEditing:(UITextView *)textView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

Cette solution a fonctionné pour moi sans aucun problème, vous pouvez l'essayer.

4
Miro Durkovic

Je viens de créer une vue texte personnalisée centrée verticalement dans Swift 3:

class VerticallyCenteredTextView: UITextView {
    override var contentSize: CGSize {
        didSet {
            var topCorrection = (bounds.size.height - contentSize.height * zoomScale) / 2.0
            topCorrection = max(0, topCorrection)
            contentInset = UIEdgeInsets(top: topCorrection, left: 0, bottom: 0, right: 0)
        }
    }
}

ref: http://geek-is-stupid.github.io/blog/2017/03/14/how-to-center-text-vertically-in-a-uitextview/

3
Tai Le

Ajoutez à la réponse Carlos, juste au cas où vous auriez du texte dans tv plus grand que la taille de tv, vous n'avez pas besoin de recentrer le texte, alors changez ce code:

tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};

pour ça:

if ([tv contentSize].height < [tv bounds].size.height) {
     tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
1
user3777977

Solution de mise en page automatique:

  1. Créez un UIView qui agit en tant que conteneur pour UITextView.
  2. Ajoutez les contraintes suivantes:
    • TextView: Aligner l'espace de début sur: Conteneur
    • TextView: Aligner l'espace de fin sur: Conteneur
    • TextView: Aligner le centre Y sur: Conteneur
    • TextView: Hauteur égale à: Conteneur, Relation: ≤
1
Etan

J'ai résolu ce problème en créant une extension au centre de la hauteur verticalement.

Swift 5:

extension UITextView {
    func centerContentVertically() {
        let fitSize = CGSize(width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
        let size = sizeThatFits(fitSize)
        let heightOffset = (bounds.size.height - size.height * zoomScale) / 2
        let positiveTopOffset = max(0, heightOffset)
        contentOffset.y = -positiveTopOffset
    }
}
0
user1039695

Vous pouvez essayer ci-dessous le code, aucun observateur obligatoire. L'observateur génère parfois des erreurs lorsque la vue est désallouée . Vous pouvez conserver ce code dans viewDidLoad, viewWillAppear ou dans viewDidAppear n'importe où. 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        UITextView *tv = txtviewDesc;
        CGFloat topCorrect = ([tv bounds].size.height - [tv contentSize].height * [tv zoomScale])/2.0;
        topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
        tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
    });
});
0
Kiran Jasvanee

Je l'ai fait comme ceci: tout d'abord, j'ai incorporé UITextView dans un UIView (cela devrait fonctionner aussi pour Mac OS). Ensuite, j'ai épinglé les quatre côtés de l'UIView externe sur les côtés de son conteneur, lui donnant une forme et une taille similaires ou égales à celles de UITextView. Ainsi, j'avais un conteneur approprié pour UITextView. Ensuite, j'ai épinglé les bordures gauche et droite de UITextView sur les côtés de UIView et lui ai donné une hauteur. Enfin, j'ai centré UITextView verticalement dans UIView. Bingo :) Maintenant, UITextView est centré verticalement dans UIView, ainsi le texte à l'intérieur de UITextView est également centré verticalement.

0
user2626974