UITextView là thành phần văn bản nhiều dòng của UIKit, kế thừa từ UIScrollView và hỗ trợ chỉnh sửa văn bản có thuộc tính với liên kết và hình ảnh nhúng. Theo tài liệu Apple Developer (2025), UITextView cung cấp UITextViewDelegate với các callback textViewDidChange, shouldInteractWithURL và shouldInteractWithTextAttachment để xử lý tương tác. Không giống UITextField, UITextView hỗ trợ cuộn, kiểu văn bản qua typingAttributes và dataDetectorTypes để tự động phát hiện liên kết, địa chỉ và ngày tháng. Đây là thành phần chính để hiển thị nội dung và nhập văn bản nhiều dòng trong ứng dụng iOS.
Các điểm chính
UITextView là một thành phần UIKit được thiết kế để hiển thị và chỉnh sửa văn bản nhiều dòng. Không giống UILabel, UITextView hỗ trợ chọn văn bản, chỉnh sửa, cuộn khi nội dung vượt quá kích thước và văn bản có thuộc tính. Là lớp con của UIScrollView, UITextView tự động bao gồm cuộn dọc và ngang khi nội dung tràn — điều này được cấu hình qua isScrollEnabled. Lớp hỗ trợ chỉnh sửa thông qua thuộc tính isEditable: đặt thành false cho chế độ chỉ đọc. UITextView hoạt động với NSLayoutManager thông qua hệ thống TextKit: NSTextStorage (lưu trữ văn bản có thuộc tính) → NSLayoutManager (bố cục dòng) → NSTextContainer (hình học vùng văn bản). Kiến trúc này cho phép bố cục tùy chỉnh với bao quanh hình ảnh, văn bản nhiều cột và bố cục văn bản phức tạp. UITextView được sử dụng để hiển thị bài viết, mô tả, nhật ký, bình luận và bất kỳ văn bản nhiều dòng nào trong iOS.
Sự khác biệt giữa UITextView và UITextField vượt xa số lượng dòng. UITextField là UIControl một dòng với hỗ trợ placeholder, chế độ xem trái/phải và nút xóa. UITextView là UIScrollView nhiều dòng với hệ thống TextKit đầy đủ cho văn bản có thuộc tính. UITextField không hỗ trợ NSAttributedString ở cấp độ hiển thị (chỉ qua attributedText với hạn chế), UITextView xử lý NSAttributedString hoàn toàn với các phông chữ, kiểu đoạn văn và hình ảnh nhúng khác nhau. UITextField có nút xóa tích hợp (clearButtonMode), UITextView thì không — cần triển khai tùy chỉnh. UITextField tự động thay đổi kích thước theo nội dung (intrinsicContentSize), UITextView yêu cầu quản lý chiều cao thủ công qua quan sát contentSize hoặc ràng buộc Auto Layout. UITextView hỗ trợ dataDetectorTypes (tự động phát hiện liên kết), UITextField hỗ trợ từ iOS 16+. Để nhập văn bản nhiều dòng (bình luận, ghi chú, tin nhắn) hãy chọn UITextView. Cho trường biểu mẫu (tên, email, mật khẩu) hãy sử dụng UITextField.
| Tính năng | UITextView | UITextField |
|---|---|---|
| Dòng | Nhiều dòng | Một dòng |
| Placeholder | Không (tùy chỉnh) | Tích hợp |
| Thuộc tính văn bản | NSAttributedString đầy đủ | Giới hạn |
| Data Detectors | Hỗ trợ đầy đủ | iOS 16+ |
| Hình ảnh inline | Qua NSTextAttachment | Không hỗ trợ |
| Cuộn | Tích hợp (UIScrollView) | Không |
| Chế độ xem Trái/Phải | Không | Tích hợp |
NSAttributedString là khả năng cơ bản của UITextView giúp phân biệt nó với UITextField và UILabel. Một chuỗi có thuộc tính cho phép đặt các phông chữ, màu sắc, khoảng cách dòng, căn chỉnh và liên kết khác nhau trong cùng một văn bản. Thuộc tính: textView.attributedText = attributedString. Các thuộc tính chính của NSAttributedString.Key: font (.systemFont, .boldSystemFont, UIFont tùy chỉnh), foregroundColor, backgroundColor, paragraphStyle (NSMutableParagraphStyle với lineSpacing, alignment, lineBreakMode), link (URL cho liên kết tương tác), underlineStyle, strikethroughStyle, shadow, baselineOffset, kern (khoảng cách chữ). Để tạo attributedString sử dụng init(string:, attributes:) hoặc NSMutableAttributedString để thêm thuộc tính vào các phạm vi khác nhau. UITextView tự động hiển thị liên kết (thuộc tính link) dưới dạng tương tác — nhấn vào sẽ kích hoạt phương thức ủy quyền shouldInteractWithURL. Để đặt lại thuộc tính cho đầu vào mới, sử dụng typingAttributes.
import UIKit
let textView = UITextView()
textView.isEditable = false
textView.isScrollEnabled = true
textView.dataDetectorTypes = [.link, .phoneNumber]
let attributedString = NSMutableAttributedString(
string: "Visit our website or call +1-800-555-0199"
)
attributedString.addAttributes([
.font: UIFont.systemFont(ofSize: 16),
.foregroundColor: UIColor.label,
], range: NSRange(location: 0, length: 11))
let linkRange = (attributedString.string as NSString)
.range(of: "website")
attributedString.addAttributes([
.link: URL(string: "https://example.com")!,
.foregroundColor: UIColor.systemBlue,
], range: linkRange)
textView.attributedText = attributedString
UITextViewDelegate là một giao thức quản lý việc chỉnh sửa và tương tác với văn bản. Các phương thức chính: textViewDidBeginEditing(textView) — được gọi khi bắt đầu chỉnh sửa; textViewDidEndEditing — khi kết thúc chỉnh sửa; textViewDidChange — mỗi khi văn bản thay đổi; textViewDidChangeSelection — khi vùng chọn thay đổi; textViewShouldInteractWithURL — khi nhấn vào liên kết (trả về Bool, cho phép chuyển hướng); textViewShouldInteractWithTextAttachment — khi nhấn vào hình ảnh nhúng. Để xử lý liên kết bên trong UITextView, ghi đè shouldInteractWithURL — trong đó bạn có thể: mở URL trong SFSafariViewController, hiển thị UIAlertController để xác nhận, xử lý lược đồ URL tùy chỉnh. Quan trọng: nếu textView.isEditable = true, các liên kết không khả dụng cho một lần chạm — cần chạm hai lần. Để có liên kết tương tác trong textView có thể chỉnh sửa, hãy sử dụng gesture recognizers.
SFSafariViewController là cách tiêu chuẩn để mở liên kết trong ứng dụng iOS. Trong phương thức shouldInteractWithURL, tạo SFSafariViewController với URL và trình bày nó theo kiểu modal hoặc qua navigationController. Đối với liên kết phổ quát, sử dụng UIApplication.shared.open(url, options) — nhưng điều này thoát khỏi ứng dụng. Để ngăn mở liên kết, trả về false trong shouldInteractWithURL và tự xử lý URL.
NSTextContainer là một phần của kiến trúc TextKit, xác định vùng hình học nơi văn bản UITextView được hiển thị. Mỗi UITextView có một textContainer tiêu chuẩn, có thể cấu hình qua: textView.textContainerInset — lề văn bản từ các cạnh textView (UIEdgeInsets); textView.textContainer.lineFragmentPadding — đệm ngang bên trong mỗi dòng; textView.textContainer.maximumNumberOfLines — giới hạn dòng (0 = không giới hạn); textView.textContainer.exclusionPaths — mảng UIBezierPath cho các vùng mà văn bản cần bao quanh (hình ảnh, hình dạng tùy chỉnh). ExclusionPaths cho phép bố cục phức tạp: văn bản bao quanh ảnh hồ sơ hình tròn, biểu tượng hoặc bất kỳ hình dạng tùy ý nào. Để có chiều cao UITextView động, hãy quan sát contentSize qua KVO hoặc ủy quyền và cập nhật các ràng buộc.
// Dynamic UITextView height via contentSize
func textViewDidChange(textView: UITextView) {
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(
CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)
)
heightConstraint.constant = newSize.height
UIView.animate(withDuration: 0.1) {
self.view.layoutIfNeeded()
}
}
// ExclusionPath for image wrapping
let imageFrame = CGRect(x: 16, y: 100, width: 80, height: 80)
let exclusionPath = UIBezierPath(rect: imageFrame)
textView.textContainer.exclusionPaths = [exclusionPath]
dataDetectorTypes là thuộc tính UITextView tự động nhận dạng và làm cho một số loại dữ liệu trong văn bản trở nên tương tác: .link (URL, email), .phoneNumber (số điện thoại), .address (địa chỉ), .calendarEvent (ngày và sự kiện), .flightNumber (số chuyến bay), .lookupSuggestion (gợi ý tìm kiếm), .trackingNumber (số theo dõi), .money (số tiền), .shipmentTrackingNumber, .all (tất cả loại). Data Detector hoạt động với NSDataDetector và URL, tự động làm nổi bật dữ liệu được phát hiện. Để tùy chỉnh xử lý, hãy đặt dataDetectorTypes và ghi đè shouldInteractWithURL trong ủy quyền. Quan trọng: dataDetectorTypes chỉ hoạt động cho URL và phoneNumber mà không cần thiết lập thêm — các loại khác có thể yêu cầu iOS 16+. Data Detector không xung đột với thuộc tính link trong NSAttributedString — cả hai cơ chế hoạt động song song. Đối với UITextView chỉ đọc có nội dung (tin tức, bài viết), hãy đặt dataDetectorTypes = [.link, .phoneNumber].
Hãy xem xét hai ví dụ thực tế về việc sử dụng UITextView trong ứng dụng iOS. Ví dụ đầu tiên — chế độ xem văn bản chỉ đọc với văn bản có thuộc tính và trình phát hiện dữ liệu để hiển thị nội dung. Ví dụ thứ hai — chế độ xem văn bản có thể chỉnh sửa với trình giữ chỗ lười biếng và chiều cao động cho biểu mẫu bình luận. Cả hai ví dụ đều sử dụng các phương pháp hay nhất của UIKit: giao thức ủy quyền, Auto Layout và TextKit.
Chế độ chỉ đọc được kích hoạt bằng cách đặt isEditable = false và isSelectable = true. Điều này cho phép người dùng chọn văn bản và nhấn vào liên kết, nhưng không chỉnh sửa. Đặt dataDetectorTypes để tự động phát hiện liên kết, backgroundColor = .clear cho nền trong suốt. Đối với nội dung dài, sử dụng attributedText với NSMutableParagraphStyle lineSpacing để dễ đọc. Thêm thuộc tính link cho các liên kết chính trong văn bản.
import UIKit
class ArticleViewController: UIViewController {
let contentView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
contentView.isEditable = false
contentView.isSelectable = true
contentView.dataDetectorTypes = [.link, .phoneNumber]
contentView.backgroundColor = .clear
contentView.textContainerInset = UIEdgeInsets(
top: 8, left: 16,
bottom: 8, right: 16
)
contentView.delegate = self
loadArticleContent()
}
func loadArticleContent() {
guard let url = URL(string: "https://example.com/article")
else { return }
// Load content from URL and set attributedText
}
}
Trình giữ chỗ cho UITextView không được tích hợp sẵn — hãy triển khai nó qua UILabel phía trên textView hoặc qua attributedText với kiểm tra isEmpty. Một cách tiếp cận đơn giản: UILabel với văn bản màu xám ẩn đi khi bắt đầu chỉnh sửa. Trong textViewDidChange, kiểm tra sự hiện diện của văn bản và ẩn/hiện trình giữ chỗ. Để có giải pháp thanh lịch hơn, hãy sử dụng thuộc tính typingAttributes để đặt màu văn bản đầu vào. Chiều cao động của textView đạt được bằng cách quan sát contentSize và cập nhật các ràng buộc — đây là mẫu tiêu chuẩn cho trò chuyện và biểu mẫu bình luận.
Câu hỏi Thường gặp
UITextView không có trình giữ chỗ tích hợp. Giải pháp tiêu chuẩn: thêm UILabel với văn bản màu xám phía trên textView. Trong textViewDidBeginEditing ẩn nhãn, trong textViewDidEndEditing hiển thị nếu văn bản trống. Thay thế: sử dụng attributedText với màu xám làm trình giữ chỗ và đặt lại khi bắt đầu chỉnh sửa. Để có giải pháp sẵn sàng, hãy sử dụng thư viện KMPlaceholderTextView hoặc ReactorKit.
Đặt textContainerInset = .zero và lineFragmentPadding = 0: textView.textContainerInset = UIEdgeInsets.zero; textView.textContainer.lineFragmentPadding = 0. Thêm vào: textView.contentInset = .zero. Điều này sẽ loại bỏ hoàn toàn lề từ các cạnh textView đến văn bản. Lưu ý rằng lineFragmentPadding mặc định là 5.
Kiểm tra: isSelectable = true (cần thiết cho tương tác), dataDetectorTypes bao gồm .link, ủy quyền không trả về false trong shouldInteractWithURL. Nếu textView.isEditable = true, các liên kết không khả dụng cho một lần chạm — cần chạm hai lần. Để có liên kết tương tác trong textView có thể chỉnh sửa, thêm UITapGestureRecognizer phía trên textView và xử lý URL thủ công.
Đặt isScrollEnabled = true và thêm ràng buộc chiều cao với giá trị cố định. Văn bản vượt quá chiều cao sẽ cuộn. Để có UX tối ưu, sử dụng maxHeight: quan sát contentSize và đặt ràng buộc chiều cao không cao hơn maxHeight. Khi vượt quá maxHeight, bật scrollEnabled. Đây là mẫu tiêu chuẩn cho trường nhập tin nhắn và bình luận.
Sử dụng NSTextAttachment: tạo một đối tượng, đặt hình ảnh và bounds, sau đó tạo NSAttributedString từ tệp đính kèm và chèn vào textView.attributedText. Ví dụ: let attachment = NSTextAttachment(); attachment.image = image; attachment.bounds = CGRect(x, y, width, height); let attributedString = NSAttributedString(attachment: attachment). Hình ảnh sẽ hiển thị nội dòng trong văn bản và cuộn cùng với nó.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm