You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现UITextView随文本自适应大小且支持长文本滚动?

How to Make UITextView Auto-Size with Content and Enable Scrolling When Text is Too Long

Got it, let's tackle this UITextView auto-sizing problem—this is such a common scenario for input fields, and I've got two reliable approaches to share with you. The core goal here is to let the text view grow with its content until it hits a maximum height, then lock it and enable scrolling.


Approach 1: Auto Layout + Dynamic Height Calculation (No Subclassing Needed)

This is my go-to method because it's straightforward and works with both Storyboard/XIB and code-created text views.

Step 1: Set Up Constraints

First, configure your UITextView's constraints:

  • Pin the top, leading, and trailing edges to your parent view (or other elements) with your desired padding (e.g., 16pt).
  • Add a minimum height constraint (e.g., 44pt—follows iOS's touch target guidelines).
  • Add a maximum height constraint (e.g., 200pt, adjust based on your UI needs) and set its priority to 750 (lower than the default 1000 required priority). This lets Auto Layout override it when the content is shorter than the max height.

Step 2: Listen for Text Changes

You can use either the UITextViewDelegate or NotificationCenter to detect when the text updates. Here's how to implement both:

Using Delegate:

import UIKit

class ViewController: UIViewController, UITextViewDelegate {
    @IBOutlet weak var inputTextView: UITextView!
    @IBOutlet weak var textViewMaxHeightConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        inputTextView.delegate = self
        inputTextView.font = .systemFont(ofSize: 16) // Set your desired font
        // Initial height check
        adjustTextViewSizeAndScroll()
    }
    
    func textViewDidChange(_ textView: UITextView) {
        adjustTextViewSizeAndScroll()
    }
    
    private func adjustTextViewSizeAndScroll() {
        // Calculate the height needed to fit all content
        let requiredHeight = inputTextView.sizeThatFits(
            CGSize(width: inputTextView.bounds.width, height: CGFloat.greatestFiniteMagnitude)
        ).height
        
        // Toggle scrolling and adjust height
        if requiredHeight <= textViewMaxHeightConstraint.constant {
            inputTextView.isScrollEnabled = false
            // Let Auto Layout use the required height (max height constraint has lower priority, so it'll be ignored here)
            inputTextView.heightAnchor.constraint(equalToConstant: requiredHeight).isActive = true
        } else {
            inputTextView.isScrollEnabled = true
            // Re-enforce the max height constraint
            textViewMaxHeightConstraint.isActive = true
        }
        
        // Force immediate layout update
        inputTextView.layoutIfNeeded()
    }
}

Using NotificationCenter:

If you prefer not to use the delegate, replace the delegate setup with this in viewDidLoad:

NotificationCenter.default.addObserver(
    self,
    selector: #selector(adjustTextViewSizeAndScroll),
    name: UITextView.textDidChangeNotification,
    object: inputTextView
)

And mark adjustTextViewSizeAndScroll with @objc.


Approach 2: Subclass UITextView (Override Intrinsic Content Size)

If you want a reusable component, subclassing UITextView to handle intrinsic size updates is a clean option.

Step 1: Create the Subclass

class AutoSizingTextView: UITextView {
    override var intrinsicContentSize: CGSize {
        // Calculate the size needed to fit all content
        let contentSize = self.sizeThatFits(
            CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude)
        )
        // Return width as "no intrinsic metric" so it respects leading/trailing constraints
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        // Invalidate intrinsic size if bounds change (e.g., device rotation)
        if bounds.size != intrinsicContentSize {
            invalidateIntrinsicContentSize()
        }
    }
}

Step 2: Use the Subclass

Set up constraints the same way as Approach 1 (min height, max height with priority 750), then add code to toggle scrolling:

class ViewController: UIViewController, UITextViewDelegate {
    @IBOutlet weak var autoSizingTextView: AutoSizingTextView!
    @IBOutlet weak var maxHeightConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        autoSizingTextView.delegate = self
        updateScrollStatus()
    }
    
    func textViewDidChange(_ textView: UITextView) {
        updateScrollStatus()
    }
    
    private func updateScrollStatus() {
        let contentHeight = autoSizingTextView.intrinsicContentSize.height
        autoSizingTextView.isScrollEnabled = contentHeight > maxHeightConstraint.constant
    }
}

Key Notes to Avoid Issues

  • Font Consistency: Make sure the font you set on the UITextView matches what you use for calculations—sizeThatFits relies on the current font to compute height.
  • Edge Insets: If you've set textContainerInset or contentInset, adjust your height calculation to account for those (add the top/bottom insets to the required height).
  • Initial State: Always run the height/scroll check once in viewDidLoad to set the correct initial state for pre-filled text.

Hope this solves your problem! If you run into edge cases (like handling attributed text or dynamic type), feel free to ask for tweaks.

内容的提问来源于stack exchange,提问作者Jay Patel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:52:12