Center text vertically in a UITextView

IphoneObjective CIosXcodeUitextview

Iphone Problem Overview


I want to center the text vertically inside a big UITextView that fills the whole screen - so that when there's little of text, say a couple of words, it is centered by height. It's not a question about centering the text (a property that can be found in IB) but about putting the text vertically right in the middle of UITextView if the text is short, so there are no blank areas in the UITextView. Can this be done? Thanks in advance!

Iphone Solutions


Solution 1 - Iphone

First add an observer for the contentSize key value of the UITextView when the view is loaded:

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

Then add this method to adjust the contentOffset every time the contentSize value changes:

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

Solution 2 - Iphone

Because UIKit is not KVO compliant, I decided to implement this as a subclass of UITextView which updates whenever the contentSize changes.

It's a slightly modified version of Carlos's answer which sets the contentInset instead of the contentOffset. In addition to being compatible with iOS 9, it also seems to be less buggy on 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)
        }
    }
}

Solution 3 - Iphone

If you don't want to use KVO you can also manually adjust offset with exporting this code to a function like this :

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

and calling it in

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

and every time you edit the text in the code. Don't forget to set the controller as the delegate

Swift 3 version:

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

Solution 4 - Iphone

For iOS 9.0.2. we'll need to set the contentInset instead. If we KVO the contentOffset, iOS 9.0.2 sets it to 0 at the last moment, overriding the changes to 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];
}

I used 0,0, and 0 for the left,bottom and right edge insets respectively. Make sure to calculate those as well for your use case.

Solution 5 - Iphone

Here's a UITextView extension that centers content vertically:

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
    }

}

Solution 6 - Iphone

You can set it up directly with only constraints:

There are 3 constraints i added to align text vertically and horizontally in constraints as below :

enter image description here

  1. Make height 0 and add constraints greater than
  2. Add vertically align to parent constraints
  3. Add horizontally align to parent constraints

enter image description here

Solution 7 - Iphone

It is the simple task using NSLayoutManager to get real text size of the NSTextContainer

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

Don't use contentSize and contentInset in your final calculations.

Solution 8 - Iphone

I just created a custom vertically centered text view 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-vertically-in-a-uitextview/

Solution 9 - Iphone

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

Solution 10 - Iphone

I have a textview that I'm using with autolayout and with setting the lineFragmentPadding and textContainerInset to zero. None of the solutions above worked in my situation. However, this works for me. Tested with 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

Solution 11 - Iphone

I have also this problem and I solved it with a UITableViewCell with UITextView. I created method in a custom UITableViewCell subclass, property 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 };

And call this method in methods:

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

This solution worked for me with no issues, you can try it.

Solution 12 - Iphone

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

Solution 13 - Iphone

Add to Carlos answer, just in case you have text in tv bigger then tv size you don't need to recenter text, so change this code:

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

to this:

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

Solution 14 - Iphone

Auto-layout solution:

  1. Create a UIView that acts as a container for the UITextView.
  2. Add the following constraints:
    • TextView: Align leading space to: Container
    • TextView: Align trailing space to: Container
    • TextView: Align center Y to: Container
    • TextView: Equal Height to: Container, Relation: ≤

Solution 15 - Iphone

You can try below code, no observer mandatorily required. observer throws error sometimes when view deallocates. You can keep this code in viewDidLoad, viewWillAppear or in viewDidAppear anywhere.

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

Solution 16 - Iphone

I did it like this: first of all, I embedded the UITextView in an UIView (this should work for mac OS too). Then I pinned all four sides of the external UIView to the sides of its container, giving it a shape and size similar or equal to that of the UITextView. Thus I had a proper container for the UITextView. Then I pinned the left and right borders of the UITextView to the sides of the UIView, and gave the UITextView a height. Finally, I centered the UITextView vertically in the UIView. Bingo :) now the UITextView is vertically centered in the UIView, hence text inside the UITextView is vertically centered too.

Solution 17 - Iphone

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

Solution 18 - Iphone

I fixed this problem by creating extension to center height vertically.

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

Solution 19 - Iphone

Solution for 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSergey GrischyovView Question on Stackoverflow
Solution 1 - IphoneCSolanaMView Answer on Stackoverflow
Solution 2 - IphoneSensefulView Answer on Stackoverflow
Solution 3 - IphoneBeninho85View Answer on Stackoverflow
Solution 4 - Iphones.kaView Answer on Stackoverflow
Solution 5 - IphoneRudolf AdamkovičView Answer on Stackoverflow
Solution 6 - IphoneKOTIOSView Answer on Stackoverflow
Solution 7 - IphonemalexView Answer on Stackoverflow
Solution 8 - IphoneTai LeView Answer on Stackoverflow
Solution 9 - IphoneIevgenView Answer on Stackoverflow
Solution 10 - Iphoneuser1687195View Answer on Stackoverflow
Solution 11 - IphoneMiro DurkovicView Answer on Stackoverflow
Solution 12 - IphoneArnaldoView Answer on Stackoverflow
Solution 13 - Iphoneuser3777977View Answer on Stackoverflow
Solution 14 - IphoneEtanView Answer on Stackoverflow
Solution 15 - IphoneKiran JasvaneeView Answer on Stackoverflow
Solution 16 - Iphoneuser2626974View Answer on Stackoverflow
Solution 17 - IphoneDan RosenstarkView Answer on Stackoverflow
Solution 18 - Iphoneuser1039695View Answer on Stackoverflow
Solution 19 - IphoneDavid HerseyView Answer on Stackoverflow