73

我想将文本垂直居中在一个填满整个屏幕的大屏幕内UITextView- 这样当文本很少时,说几句话,它就会以高度为中心。这不是关于文本居中的问题(可以在 IB 中找到的属性),而是关于将文本垂直放在中间的问题,UITextView 如果文本很短,因此UITextView. 这可以做到吗?提前致谢!

4

19 回答 19

62

首先为视图加载时的contentSize键值添加一个观察者:UITextView

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

然后添加此方法以调整contentOffset每次contentSize值更改时:

-(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};
}
于 2012-09-25T21:26:03.827 回答
56

因为UIKit 不兼容 KVO,所以我决定将其实现为一个子类,UITextView每当contentSize更改时它就会更新。

这是卡洛斯答案的一个稍微修改过的版本,它设置了contentInset而不是contentOffset. 除了与 iOS 9 兼容之外,它在 iOS 8.4 上的 bug 似乎也更少。

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)
        }
    }
}
于 2015-10-29T22:13:47.380 回答
26

如果您不想使用 KVO,您也可以通过将此代码导出到如下函数来手动调整偏移量:

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

并调用它

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

每次编辑代码中的文本时。不要忘记将控制器设置为委托

斯威夫特 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)
}
于 2015-05-06T20:39:20.707 回答
20

对于 iOS 9.0.2。我们需要设置 contentInset 。如果我们对 contentOffset 进行 KVO,iOS 9.0.2 会在最后一刻将其设置为 0,从而覆盖对 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];
}

我分别将 0,0 和 0 用于左、下和右边缘插图。确保也为您的用例计算这些。

于 2015-10-25T22:04:57.483 回答
11

您可以仅使用约束直接设置它:

我添加了 3 个约束来在约束中垂直和水平对齐文本,如下所示:

在此处输入图像描述

  1. 将高度设为 0 并添加大于
  2. 添加垂直对齐到父约束
  3. 添加水平对齐到父约束

在此处输入图像描述

于 2017-08-10T13:06:25.547 回答
10

这是一个UITextView垂直居中内容的扩展:

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
    }

}
于 2016-08-09T15:42:49.003 回答
8

我刚刚在 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)
        }
    }
}

参考:https ://geek-is-stupid.github.io/2017-05-15-how-to-center-text-vertically-in-a-uitextview/

于 2017-03-14T04:37:14.647 回答
6
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)
}
于 2015-08-14T19:42:49.013 回答
6

我有一个文本视图,我正在使用自动布局并将 and 设置lineFragmentPadding为零textContainerInset。上述解决方案均不适用于我的情况。但是,这对我有用。使用 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
于 2016-02-05T17:06:12.070 回答
6

NSLayoutManager这是用于获取实际文本大小的简单任务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)
    }
}

不要在最终计算中使用contentSize和。contentInset

于 2020-12-11T11:05:28.273 回答
5

我也有这个问题,我用UITableViewCellwith解决了UITextView。我在自定义UITableViewCell子类中创建了方法,属性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 };

并在方法中调用此方法:

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

这个解决方案对我来说没有问题,你可以试试。

于 2013-09-24T07:59:51.597 回答
5

斯威夫特 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)

    }
于 2017-03-21T23:18:50.887 回答
2

添加到 Carlos 答案,以防万一电视中的文本大于电视大小,您不需要重新定位文本,因此更改此代码:

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

对此:

if ([tv contentSize].height < [tv bounds].size.height) {
     tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}
于 2014-06-26T06:10:05.847 回答
2

自动布局解决方案:

  1. 创建一个 UIView 作为 UITextView 的容器。
  2. 添加以下约束:
    • TextView:将前导空格对齐到:容器
    • TextView:将尾随空格对齐:容器
    • TextView:将中心 Y 对齐到:容器
    • TextView:等高到:容器,关系:≤</li>
于 2015-03-08T10:33:10.277 回答
1

你可以试试下面的代码,不需要观察者。当视图解除分配时,观察者有时会抛出错误。您可以将此代码保存在 viewDidLoad、viewWillAppear 或任何地方的 viewDidAppear 中。

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};
    });
});
于 2015-07-27T12:23:00.423 回答
0

我是这样做的:首先,我将 UITextView 嵌入到 UIView 中(这也适用于 mac OS)。然后我将外部 UIView 的所有四个侧面都固定到其容器的侧面,使其形状和大小类似于或等于 UITextView 的形状和大小。因此,我有一个合适的 UITextView 容器。然后我将 UITextView 的左右边框固定到 UIView 的两侧,并给 UITextView 一个高度。最后,我将 UITextView 在 UIView 中垂直居中。Bingo :) 现在 UITextView 在 UIView 中垂直居中,因此 UITextView 内的文本也垂直居中。

于 2016-09-28T06:18:13.533 回答
-1

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
于 2015-05-18T21:02:47.867 回答
-1

我通过垂直创建中心高度的扩展来解决这个问题。

斯威夫特 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
    }
}
于 2019-07-14T23:50:45.693 回答
-4

RubyMotion中iOS10的解决方案:

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
于 2016-09-20T19:35:43.830 回答