Fix: Unwanted Padding and Margins in UITextView on iOS

Written by

in

Quick answer: To remove all default padding and margins inside a UITextView, set textView.textContainerInset = .zero and textView.textContainer.lineFragmentPadding = 0. Both properties are required because UITextView applies vertical insets at the container level and horizontal padding on each line fragment.

Why this happens

Unlike UILabel, UITextView is built on top of TextKit (NSTextContainer, NSLayoutManager, and NSTextStorage) and adds two separate layers of internal spacing by default.

First, textContainerInset adds inset space around the text container—most noticeably at the top and bottom of the view. Second, textContainer.lineFragmentPadding adds horizontal padding to the leading and trailing edges of every line of text.

Because these two properties belong to the text container rather than the scroll view itself, setting contentInset = .zero does not align the text flush with the view’s bounds. If you are calculating custom text paging, aligning a UITextView with a UILabel, or matching tight design specs, those extra margins will throw off your layout.

How to fix it

1. Set textContainerInset and lineFragmentPadding to zero

To make text sit flush against all four edges of a UITextView, zero out both textContainerInset and textContainer.lineFragmentPadding in code (for example, in viewDidLoad() or your view’s setup method):

import UIKit

func configureTextView(_ textView: UITextView) {
    textView.textContainerInset = .zero
    textView.textContainer.lineFragmentPadding = 0
}

Here is what each line controls:

  • textContainerInset = .zero removes the top, bottom, left, and right insets around the text container.
  • textContainer.lineFragmentPadding = 0 removes the horizontal padding inside each line fragment so the first and last characters align with the view’s leading and trailing edges.

If your layout also adjusts scroll insets, you can explicitly set textView.contentInset = .zero, though textContainerInset and lineFragmentPadding are the two properties responsible for the text margins.

2. Create a reusable UITextView subclass for Storyboards

Xcode’s Attributes Inspector does not expose textContainer.lineFragmentPadding for a standard UITextView. If you configure your views in a Storyboard or XIB, subclass UITextView and apply both settings in layoutSubviews():

import UIKit

@IBDesignable
final class ZeroPaddingTextView: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        removeDefaultPadding()
    }

    private func removeDefaultPadding() {
        textContainerInset = .zero
        textContainer.lineFragmentPadding = 0
    }
}

To use it in Interface Builder:

  1. Select your UITextView in the Storyboard or XIB.
  2. Open the Identity Inspector in the right sidebar.
  3. Set Class to ZeroPaddingTextView.

Because the class is marked @IBDesignable, the updated zero-margin layout renders both in Xcode’s canvas and at runtime.

3. Handle scrolling and dynamic height edge cases

When a UITextView resizes dynamically to fit its content—such as inside a self-sizing UITableViewCell or an expanding input bar—you may still see extra space at the bottom unless you disable scrolling:

textView.isScrollEnabled = false

When isScrollEnabled is true, UITextView behaves like a UIScrollView and does not report an intrinsic content size based on its text. Disabling scrolling lets Auto Layout calculate the exact height from the text content.

If you are using UITextView to display read-only text with custom behaviors, two additional TextKit tweaks are often helpful:

  • Truncate overflowing text with an ellipsis:
textView.textContainer.lineBreakMode = .byTruncatingTail
  • Collapse height to zero when empty: By default, a non-scrolling UITextView with empty text still reserves the height of one line. Override intrinsicContentSize in your subclass to collapse it completely:
override var intrinsicContentSize: CGSize {
    var size = super.intrinsicContentSize
    if text.isEmpty {
        size.height = 0
    }
    return size
}

4. Switch to UILabel for static display text

If you do not need user editing, text selection, scrolling, or automatic link/phone detection, UILabel is usually a better fit. UILabel has no default internal padding, respects Auto Layout intrinsicContentSize cleanly, and avoids TextKit’s sizing quirks.

let label = UILabel()
label.numberOfLines = 0
label.text = "Multi-line text with zero default padding."

How to prevent it

  • Centralize your configuration: Use a shared UITextView subclass or factory function so every text view in your app starts with consistent textContainerInset and lineFragmentPadding values.
  • Choose UILabel when possible: Reserve UITextView for editable, selectable, scrollable, or link-detecting text.
  • Disable isScrollEnabled for Auto Layout sizing: Whenever a UITextView is expected to grow or shrink with its content, turn off scrolling so Auto Layout can read its intrinsicContentSize.
  • Verify with Dynamic Type: Test your layout with larger accessibility text sizes to confirm your edge-to-edge alignment holds without clipping.

FAQ

Why doesn’t setting contentInset = .zero remove UITextView padding?

contentInset only adjusts the outer scroll view’s inset area, whereas UITextView adds its default text margins inside the TextKit container. You need textContainerInset = .zero and textContainer.lineFragmentPadding = 0 to remove the padding around the text itself.

How do I remove UITextView padding in Storyboard without writing code?

Interface Builder does not expose textContainer.lineFragmentPadding in the Attributes Inspector. The cleanest way to handle it in Storyboards is to create a small UITextView subclass that zeroes out both properties and assign that class in the Identity Inspector.

Why does my UITextView still have extra space at the bottom?

If you already zeroed textContainerInset and lineFragmentPadding and still see extra vertical space, check isScrollEnabled. For Auto Layout to size a UITextView tightly around its text, isScrollEnabled must be set to false.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.