在 UITextView 中垂直居中显示文本

我想把文本 垂直的放在一个占满整个屏幕的大 UITextView中心,这样当文本很少的时候,比如说几个单词,它就会被高度放在中心。 这不是关于文本居中的问题(这个属性可以在 IB 中找到) ,而是关于将文本 垂直的放在 UITextView 如果的中间,因为文本很短,所以在 UITextView中没有空白区域。 这样可以吗? 提前谢谢!

73775 次浏览

加载视图时,首先为 UITextViewcontentSize键值添加一个观察器:

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

然后添加这个方法,以便每次 contentSize值变化时调整 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.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
}

我也有这个问题,我解决了它与 UITableViewCellUITextView。我在自定义的 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

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

添加到卡洛斯回答,只是为了防止你有电视大于电视大小的文本,你不需要重新输入文本,所以改变这个代码:

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

回到这里:

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

自动布局解决方案:

  1. 创建一个充当 UITextView 容器的 UIView。
  2. 增加以下限制:
    • TextView: 将前导空间与容器对齐
    • TextView: 将尾随空间与容器对齐
    • TextView: 将 Y 中心与容器对齐
    • TextView: 等高: 容器,关系: ≤

如果你不想使用 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];
}

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

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

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

您可以尝试下面的代码,没有观察者强制要求。观察者有时会在视图释放时抛出错误。 您可以将此代码保存在 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};
});
});
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)
}

IOS9.0.2.我们需要改为设置 contentInset。如果我们 KVO contentOffset,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。一定要为您的用例计算这些数据。

因为是 UIKit 不符合 KVO 标准,所以我决定将它实现为 UITextView的一个子类,每当 contentSize发生变化时它就会更新。

它是 卡洛斯的回答的一个稍微修改过的版本,它设置的是 contentInset而不是 contentOffset。除了 与 iOS9兼容之外,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)
}
}
}

我有一个文本视图,我使用自动布局和设置 lineFragmentPaddingtextContainerInset为零。以上的解决方法在我的情况下都不管用。不过,这对我有用。使用 iOS9进行测试

@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

下面是一个 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
}


}

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

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

我刚在 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/

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


}

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

有3个约束,我添加到对齐文本垂直和水平约束如下:

enter image description here

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

enter image description here

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

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

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

在最后的计算中不要使用 contentSizecontentInset