如何在UITextView中丢失边距/填充?

问题描述 投票:444回答:20

我的iOS应用程序中有一个UITextView,它显示了大量的文本。

然后我使用UITextView的offset margin参数来分页这个文本。

我的问题是UITextView的填充使我的计算混乱,因为它似乎根据我使用的字体大小和字体而有所不同。

因此,我提出一个问题:是否有可能删除围绕UITextView内容的填充?

期待听到您的回复!

ios iphone cocoa-touch uikit uitextview
20个回答
292
投票

Up-to-date for 2019

这是iOS中最愚蠢的错误之一。

下面的类,UITextViewFixed通常是一个合理的解决方案。

这是班级:

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    }
}

不要忘记在Inspector中关闭scrollEnabled!

  1. 该解决方案在故事板中正常运行
  2. 该解决方案在运行时正常运行

That's it, you're done.

一般来说,在大多数情况下,这应该是您所需要的。

即使您正在快速更改文本视图的高度,这通常也可以满足您的需求。

(动态更改高度的常见示例是在用户输入时更改它。)

这是来自Apple的破坏的UITextView ......

这是UITextViewFixed

请注意,当然你必须

turn off scrollEnabled in the Inspector!

别忘了关闭scrollEnabled! :)


Some further issues

(1)在一些不寻常的情况下 - 例如,一些具有动态变化高度的灵活单元高度表视图的案例 - Apple做了一件奇怪的事情:它们在底部增加了额外的空间。不完全是!这必须是iOS中最令人愤怒的事情之一。

这是一个“快速修复”添加,通常有助于这种疯狂。

...
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0

        // this is not ideal, but you can sometimes use this
        // to fix the "extra space at the bottom" insanity
        var b = bounds
        let h = sizeThatFits(CGSize(
           width: bounds.size.width,
           height: CGFloat.greatestFiniteMagnitude)
       ).height
       b.size.height = h
       bounds = b
 ...

(2)有时,为了解决另一个微妙的Apple麻烦,你必须添加:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false)
}

(3)可以说,我们应该补充:

contentInset = UIEdgeInsets.zero

就在.lineFragmentPadding = 0UITextViewFixed之后。

但是......相信与否......它只是在当前的iOS中不起作用! (检查在2019年。)将来可能需要添加该行。

UITextView在iOS中被打破的事实是所有移动计算中最奇怪的事情之一。这个问题十周年纪念日仍然没有修复!

最后,这里有一个类似TextField的提示:https://stackoverflow.com/a/43099816/294884


9
投票

对于iOS 10,以下行适用于顶部和底部填充删除。

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

7
投票

最新的Swift:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0

6
投票

这是Fattie非常有用的答案的更新版本。它增加了2条非常重要的线条,帮助我在iOS 10和11上完成了完美的布局(也可能在较低的版本上)。 d

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        translatesAutoresizingMaskIntoConstraints = true
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
        translatesAutoresizingMaskIntoConstraints = false
    }
}

重要的是两个translatesAutoresizingMaskIntoConstraints = <true/false>声明!

在我的所有情况下,这令人惊讶地消除了所有利润!

虽然textView不是第一响应者,但可能会发生一些奇怪的底部边缘,使用接受的答案中提到的sizeThatFits方法无法解决。 当突然进入textView时,奇怪的底部边缘消失了,一切看起来应该如此,但只有textView得到firstResponder

所以我读了here on SO,在调用之间手动设置帧/边界时启用和禁用translatesAutoresizingMaskIntoConstraints确实有帮助。

幸运的是,这不仅适用于框架设置,而是将两条setup()夹在两个translatesAutoresizingMaskIntoConstraints电话之间!

例如,在使用systemLayoutSizeFitting上的UIView计算视图的框架时,这非常有用。返回正确的尺寸(之前没有)!

如在最初提到的答案: 不要忘记在Inspector中关闭scrollEnabled! 该解决方案在故事板以及运行时都能正常工作。

就是这样,现在你真的完成了!


3
投票

对于swift 4,Xcode 9

使用以下函数可以更改UITextView中文本的边距/填充

public func UIEdgeInsetsMake(_ top:CGFloat,_ left:CGFloat,_ bottom:CGFloat,_ right:CGFloat) - > UIEdgeInsets

所以在这种情况下是

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

2
投票

做插图解决方案我仍然在右侧和底部填充。文本对齐也导致了问题。我找到的唯一可靠的火方法是将文本视图放在另一个被剪裁到边界的视图中。


2
投票

这是一个简单的小扩展,它将从您应用中的每个文本视图中删除Apple的默认边距。

注意:Interface Builder仍会显示旧的边距,但您的应用程序将按预期工作。

extension UITextView {

   open override func awakeFromNib() {
      super.awakeFromNib();
      removeMargins();
   }

   /** Removes the Apple textview margins. */
   public func removeMargins() {
      self.contentInset = UIEdgeInsetsMake(
         0, -textContainer.lineFragmentPadding,
         0, -textContainer.lineFragmentPadding);
   }
}

0
投票

对我来说(iOS 11和Xcode 9.4.1)神奇的是将textView.font属性设置为UIFont.preferred(forTextStyle:UIFontTextStyle)样式,以及@Fattie提到的第一个答案。但@Fattie的答案在我设置textView.font属性之后才起作用,否则UITextView会保持不规律的行为。


0
投票

如果有人在寻找最新的Swift版本,那么下面的代码可以正常使用Xcode 10.2和Swift 4.2

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

-1
投票

我找到了另一种方法,从UITextView的子视图中获取文本视图,并在子类的layoutSubview方法中进行设置:

- (void)layoutSubviews {
    [super layoutSubviews];

    const int textViewIndex = 1;
    UIView *textView = [self.subviews objectAtIndex:textViewIndex];
    textView.frame = CGRectMake(
                                 kStatusViewContentOffset,
                                 0.0f,
                                 self.bounds.size.width - (2.0f * kStatusViewContentOffset),
                                 self.bounds.size.height - kStatusViewContentOffset);
}

-1
投票

textView滚动也会影响文本的位置,使其看起来不是垂直居中。我设法通过禁用滚动并将顶部插入设置为0来使文本在视图中居中:

    textView.scrollEnabled = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);

出于某种原因,我还没有想到它,在打字开始之前光标仍未居中,但是当我开始输入时,文本会立即居中。


769
投票

对于iOS 7.0,我发现contentInset技巧不再有效。这是我用来摆脱iOS 7中的边距/填充的代码。

这会将文本的左边缘带到容器的左边缘:

textView.textContainer.lineFragmentPadding = 0

这会导致文本顶部与容器顶部对齐

textView.textContainerInset = .zero

需要两条线才能完全去除边距/填充。


-5
投票
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];

251
投票

这个解决方案是在2009年IOS 3.0发布时编写的。它不再适用。

我遇到了完全相同的问题,最后我不得不结束使用

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);

其中nameField是UITextView。我碰巧使用的字体是Helvetica 16点。它只是我绘制的特定字段大小的自定义解决方案。这使得左偏移与左侧齐平,并且顶部偏移在我想要它的框中绘制。

此外,这似乎只适用于使用默认对齐的UITextViews,即。

nameField.textAlignment = NSTextAlignmentLeft;

例如,与右边对齐,UIEdgeInsetsMake似乎对右边缘没有任何影响。

至少,使用.contentInset属性允许您将字段放置在“正确”位置,并在不偏移UITextViews的情况下适应偏差。


70
投票

建立一些已经给出的好答案,这里是一个纯粹基于Interface Builder的解决方案,它利用用户定义的运行时属性并在iOS 7.0+中工作:


45
投票

在iOS 5上,UIEdgeInsetsMake(-8,-8,-8,-8);看起来效果很好。


40
投票

我肯定会避免涉及硬编码值的任何答案,因为实际的边距可能会随着用户字体大小设置等而改变。

这是@ user1687195的答案,写的没有修改textContainer.lineFragmentPadding(因为docs状态这不是预期的用法)。

这适用于iOS 7及更高版本。

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding, 
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding);

这实际上是相同的结果,只是有点清洁,因为它不会滥用lineFragmentPadding属性。


18
投票

Storyboard或Interface Builder解决方案,使用用户定义的运行时属性。更新。添加了iOS7.1和iOS6.1屏幕截图,其中contentInset = {{-10,-5},{0,0}}


15
投票

所有这些答案都解决了标题问题,但我想针对OP问题正文中提出的问题提出一些解决方案。

Size of Text Content

计算UITextView内文本大小的快速方法是使用NSLayoutManager

UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;

这给出了总可滚动内容,其可能大于UITextView的框架。我发现这比textView.contentSize准确得多,因为它实际上计算了文本占用的空间。例如,给出一个空的UITextView

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)

Line Height

UIFont有一个属性,可以快速获取给定字体的行高。因此,您可以快速找到UITextView中文本的行高:

UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;

Calculating Visible Text Size

确定实际可见的文本量对于处理“分页”效果很重要。 UITextView有一个名为textContainerInset的财产,它实际上是实际的UITextView.frame和文本本身之间的边界。要计算可见框架的实际高度,您可以执行以下计算:

UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;

Determining Paging Size

最后,既然你有可见的文字大小和内容,你可以通过从textHeight中减去textSize来快速确定你的偏移应该是什么:

// where n is the page number you want
CGFloat pageOffsetY = textSize - textHeight * (n - 1);
textView.contentOffset = CGPointMake(textView.contentOffset.x, pageOffsetY);

// examples
CGFloat page1Offset = 0;
CGFloat page2Offset = textSize - textHeight
CGFloat page3Offset = textSize - textHeight * 2

使用所有这些方法,我没有碰到我的插图,我能够去插入符号或文本中我想要的任何地方。


11
投票

你可以使用textContainerInsetUITextView属性:

textView.textContainerInset = UIEdgeInsetsMake(10,10,10,10);

(上,左,下,右)

© www.soinside.com 2019 - 2024. All rights reserved.