Tekst verticaal centreren in een UITextView

Ik wil de tekst verticaalcentreren in een grote UITextViewdie het hele scherm vult – zodat wanneer er weinig tekst is, zeg maar een paar woorden, het is gecentreerd op hoogte.
Het gaat niet om het centreren van de tekst (een eigenschap die in IB te vinden is) maar om het verticaalin het midden van UITextViewifplaatsen em>de tekst is kort, dus er zijn geen lege gebieden in de UITextView.
Kan dit? Bij voorbaat dank!


Antwoord 1, autoriteit 100%

Voeg eerst een waarnemer toe voor de sleutelwaarde contentSizevan de UITextViewwanneer de weergave is geladen:

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

Voeg vervolgens deze methode toe om de contentOffsetaan te passen telkens wanneer de waarde van contentSizeverandert:

-(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};
}

Antwoord 2, autoriteit 90%

Omdat UIKit niet KVO-compatibel is, heb ik besloten dit te implementeren als een subklasse van UITextViewdie wordt bijgewerkt wanneer de contentSizeverandert.

Het is een licht aangepaste versie van Carlos’ antwoorddie de contentInsetin plaats van de contentOffset. Behalve dat het compatibel is met iOS 9, lijkt het ook minder buggy te zijn op 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)
        }
    }
}

Antwoord 3, autoriteit 42%

Als je KVO niet wilt gebruiken, kun je de offset ook handmatig aanpassen door deze code te exporteren naar een functie als deze:

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

en het aanroepen

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

en elke keer dat u de tekst in de code bewerkt. Vergeet niet de controller in te stellen als de gedelegeerde

Swift 3-versie:

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

Antwoord 4, autoriteit 32%

Voor iOS 9.0.2. we moeten in plaats daarvan de contentInset instellen. Als we de contentOffset KVO, zet iOS 9.0.2 deze op het laatste moment op 0, waardoor de wijzigingen in contentOffset worden genegeerd.

-(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];
}

Ik heb 0,0 en 0 gebruikt voor respectievelijk de linker-, onder- en rechterrand. Zorg ervoor dat u die ook berekent voor uw gebruiksscenario.


Antwoord 5, autoriteit 18%

Je kunt het rechtstreeks instellen met alleen beperkingen:

Er zijn 3 beperkingen die ik heb toegevoegd om tekst verticaal en horizontaal uit te lijnen in beperkingen zoals hieronder:

  1. Maak hoogte 0 en voeg beperkingen toe die groter zijn dan
  2. Verticaal uitlijnen toevoegen aan bovenliggende beperkingen
  3. Horizontaal uitlijnen toevoegen aan bovenliggende beperkingen


Antwoord 6, autoriteit 15%

Hier is een UITextView-extensie die inhoud verticaal centreert:

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

Antwoord 7, autoriteit 13%

Ik heb zojuist een aangepaste verticaal gecentreerde tekstweergave gemaakt in 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: https://geek-is-stupid.github.io/2017-05-15-how-to-center-text-vertical-in-a-uitextview/


Antwoord 8, autoriteit 10%

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

Antwoord 9, autoriteit 10%

Ik heb een tekstweergave die ik gebruik met autolayout en met het instellen van de lineFragmentPaddingen textContainerInsetop nul. Geen van de bovenstaande oplossingen werkte in mijn situatie. Dit werkt echter voor mij. Getest met 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

Antwoord 10, autoriteit 10%

Het is de eenvoudige taak om NSLayoutManagerte gebruiken om de echte tekstgrootte van de NSTextContainer

te krijgen

class VerticallyCenteredTextView: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        let rect = layoutManager.usedRect(for: textContainer)
        let topInset = (bounds.size.height - rect.height) / 2.0
        textContainerInset.top = max(0, topInset)
    }
}

Gebruik contentSizeen contentInsetniet in je uiteindelijke berekeningen.


Antwoord 11, autoriteit 8%

Ik heb ook dit probleem en ik heb het opgelost met een UITableViewCellmet UITextView. Ik heb een methode gemaakt in een aangepaste UITableViewCell-subklasse, eigenschap 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 };

En noem deze methode in methodes:

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

Deze oplossing werkte voor mij zonder problemen, je kunt het proberen.


Antwoord 12, autoriteit 8%

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

Antwoord 13, autoriteit 3%

Toevoegen aan Carlos-antwoord, voor het geval je tekst in tv groter dan tv-formaat hebt, hoef je de tekst niet opnieuw te plaatsen, dus verander deze code:

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

naar dit:

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

Antwoord 14, autoriteit 3%

Oplossing voor automatische lay-out:

  1. Maak een UIView die fungeert als een container voor de UITextView.
  2. Voeg de volgende beperkingen toe:
    • TextView: voorloopspatie uitlijnen met: container
    • TextView: volg spatie uitlijnen met: container
    • TextView: centreer Y uitlijnen op: container
    • TextView: gelijke hoogte als: container, relatie: ≤

Antwoord 15, autoriteit 2%

Je kunt onderstaande code proberen, er is geen waarnemer verplicht. waarnemer gooit soms een fout wanneer de weergave wordt gedealloceerd.
U kunt deze code overal in viewDidLoad, viewWillAppear of in viewDidAppear bewaren.

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

Antwoord 16

Ik deed het als volgt: allereerst heb ik de UITextView ingesloten in een UIView (dit zou ook moeten werken voor mac OS). Vervolgens heb ik alle vier de zijden van de externe UIView aan de zijkanten van de container vastgemaakt, waardoor deze een vorm en grootte heeft die vergelijkbaar is met of gelijk is aan die van de UITextView. Zo had ik een goede container voor de UITextView. Vervolgens heb ik de linker- en rechterrand van de UITextView aan de zijkanten van de UIView vastgemaakt en de UITextView een hoogte gegeven. Ten slotte heb ik de UITextView verticaal gecentreerd in de UIView. Bingo 🙂 nu is de UITextView verticaal gecentreerd in de UIView, dus tekst in de UITextView is ook verticaal gecentreerd.


Antwoord 17

UITextView+VerticalAlignment.h

//  UITextView+VerticalAlignment.h
//  (c) The Internet 2015
#import <UIKit/UIKit.h>
@interface UITextView (VerticalAlignment)
- (void)alignToVerticalCenter;
- (void)disableAlignment;
@end

UITextView+VerticalAlignment.m

#import "UITextView+VerticalAlignment.h"
@implementation UITextView (VerticalAlignment)
- (void)alignToVerticalCenter {
    [self addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:NULL];
}
- (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};
}
- (void)disableAlignment {
    [self removeObserver:self forKeyPath:@"contentSize"];
}
@end

Antwoord 18

Ik heb dit probleem opgelost door een verticale verlenging tot middenhoogte te maken.

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

Antwoord 19

Oplossing voor iOS10 in RubyMotion:

class VerticallyCenteredTextView < UITextView
    def init
        super
    end
    def layoutSubviews
        self.recenter
    end
    def recenter
        contentSize = self.sizeThatFits(CGSizeMake(self.bounds.size.width, Float::MAX))
        topCorrection = (self.bounds.size.height - contentSize.height * self.zoomScale) / 2.0;
        topCorrection = 0 if topCorrection < 0
        self.contentInset = UIEdgeInsetsMake(topCorrection,  0, 0, 0)
    end
end

Other episodes