UITextView เป็นคอมโพเนนต์ข้อความหลายบรรทัดของ UIKit ที่สืบทอดจาก UIScrollView และรองรับการแก้ไขข้อความที่มีคุณสมบัติพร้อมการฝังลิงก์และรูปภาพ ตามเอกสารของ Apple Developer (2025) UITextView มี UITextViewDelegate พร้อม callback textViewDidChange, shouldInteractWithURL และ shouldInteractWithTextAttachment สำหรับจัดการการโต้ตอบ แตกต่างจาก UITextField ตรงที่ UITextView รองรับการเลื่อน สไตล์ข้อความผ่าน typingAttributes และ dataDetectorTypes สำหรับการตรวจจับลิงก์ ที่อยู่ และวันที่โดยอัตโนมัติ นี่คือคอมโพเนนต์หลักสำหรับแสดงเนื้อหาและป้อนข้อความหลายบรรทัดในแอปพลิเคชัน iOS
ประเด็นสำคัญ
UITextView เป็นคอมโพเนนต์ UIKit ที่ออกแบบมาเพื่อแสดงและแก้ไขข้อความหลายบรรทัด แตกต่างจาก UILabel ตรงที่ UITextView รองรับการเลือกข้อความ การแก้ไข การเลื่อนเมื่อเนื้อหาเกินขนาด และข้อความที่มีคุณสมบัติ ในฐานะคลาสย่อยของ UIScrollView UITextView จะรวมการเลื่อนแนวตั้งและแนวนอนโดยอัตโนมัติเมื่อเนื้อหาล้น — ซึ่งกำหนดค่าผ่าน isScrollEnabled คลาสรองรับการแก้ไขผ่านคุณสมบัติ isEditable: ตั้งค่าเป็น false สำหรับโหมดอ่านอย่างเดียว UITextView ทำงานกับ NSLayoutManager ผ่านระบบ TextKit: NSTextStorage (การจัดเก็บข้อความที่มีคุณสมบัติ) → NSLayoutManager (เลย์เอาต์บรรทัด) → NSTextContainer (เรขาคณิตพื้นที่ข้อความ) สถาปัตยกรรมนี้อนุญาตให้มีเลย์เอาต์ที่กำหนดเองพร้อมการพันรูปภาพ ข้อความหลายคอลัมน์ และเลย์เอาต์ข้อความที่ซับซ้อน UITextView ใช้สำหรับแสดงบทความ คำอธิบาย บันทึก ความคิดเห็น และข้อความหลายบรรทัดใด ๆ ใน iOS
ความแตกต่างระหว่าง UITextView และ UITextField อยู่ไกลเกินกว่าจำนวนบรรทัด UITextField เป็น UIControl บรรทัดเดียวที่รองรับตัวยึดตำแหน่ง มุมมองซ้าย/ขวา และปุ่มล้าง UITextView เป็น UIScrollView หลายบรรทัดที่มีระบบ TextKit เต็มรูปแบบสำหรับข้อความที่มีคุณสมบัติ UITextField ไม่รองรับ NSAttributedString ในระดับการแสดงผล (เฉพาะผ่าน attributedText ที่มีข้อจำกัด) UITextView จัดการ NSAttributedString อย่างสมบูรณ์ด้วยแบบอักษร สไตล์ย่อหน้า และรูปภาพที่ฝังต่างกัน UITextField มีปุ่มล้างในตัว (clearButtonMode) แต่ UITextView ไม่มี — จำเป็นต้องมีการใช้งานที่กำหนดเอง UITextView ปรับขนาดโดยอัตโนมัติตามเนื้อหา (intrinsicContentSize) UITextView ต้องการการจัดการความสูงด้วยตนเองผ่านการสังเกต contentSize หรือข้อจำกัด Auto Layout UITextView รองรับ dataDetectorTypes (การตรวจจับลิงก์อัตโนมัติ) UITextField รองรับตั้งแต่ iOS 16+ สำหรับการป้อนข้อความหลายบรรทัด (ความคิดเห็น บันทึก ข้อความ) ให้เลือก UITextView สำหรับฟิลด์ฟอร์ม (ชื่อ อีเมล รหัสผ่าน) ให้ใช้ UITextField
| คุณสมบัติ | UITextView | UITextField |
|---|---|---|
| บรรทัด | หลายบรรทัด | บรรทัดเดียว |
| ตัวยึดตำแหน่ง | ไม่มี (กำหนดเอง) | ในตัว |
| คุณสมบัติข้อความ | NSAttributedString เต็ม | จำกัด |
| Data Detectors | รองรับเต็ม | iOS 16+ |
| รูปภาพอินไลน์ | ผ่าน NSTextAttachment | ไม่รองรับ |
| การเลื่อน | ในตัว (UIScrollView) | ไม่มี |
| มุมมองซ้าย/ขวา | ไม่มี | ในตัว |
NSAttributedString เป็นความสามารถพื้นฐานของ UITextView ที่ทำให้แตกต่างจาก UITextField และ UILabel สตริงที่มีคุณสมบัติอนุญาตให้ตั้งค่าแบบอักษร สี ระยะห่างบรรทัด การจัดตำแหน่ง และลิงก์ต่าง ๆ ภายในข้อความเดียว คุณสมบัติ: textView.attributedText = attributedString คุณสมบัติหลักของ NSAttributedString.Key: font (.systemFont, .boldSystemFont, UIFont ที่กำหนดเอง), foregroundColor, backgroundColor, paragraphStyle (NSMutableParagraphStyle ที่มี lineSpacing, alignment, lineBreakMode), link (URL สำหรับลิงก์โต้ตอบ), underlineStyle, strikethroughStyle, shadow, baselineOffset, kern (ระยะห่างตัวอักษร) ในการสร้าง attributedString ให้ใช้ init(string:, attributes:) หรือ NSMutableAttributedString เพื่อเพิ่มคุณสมบัติให้กับช่วงต่าง ๆ UITextView จะแสดงลิงก์ (คุณสมบัติ link) โดยอัตโนมัติเป็นแบบโต้ตอบ — การกดจะเรียกเมธอดตัวแทน shouldInteractWithURL ในการรีเซ็ตคุณสมบัติสำหรับการป้อนใหม่ให้ใช้ 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 เป็นโปรโตคอลที่จัดการการแก้ไขและการโต้ตอบกับข้อความ เมธอดหลัก: textViewDidBeginEditing(textView) — เรียกเมื่อเริ่มแก้ไข; textViewDidEndEditing — เมื่อสิ้นสุดการแก้ไข; textViewDidChange — ทุกครั้งที่ข้อความเปลี่ยนแปลง; textViewDidChangeSelection — เมื่อการเลือกเปลี่ยนแปลง; textViewShouldInteractWithURL — เมื่อแตะลิงก์ (คืนค่า Bool อนุญาตการเปลี่ยน); textViewShouldInteractWithTextAttachment — เมื่อแตะรูปภาพที่ฝัง ในการจัดการลิงก์ภายใน UITextView ให้แทนที่ shouldInteractWithURL — ในนั้นคุณสามารถ: เปิด URL ใน SFSafariViewController, แสดง UIAlertController เพื่อยืนยัน, จัดการสคีมา URL ที่กำหนดเอง สำคัญ: ถ้า textView.isEditable = true ลิงก์จะไม่พร้อมใช้งานสำหรับการแตะครั้งเดียว — ต้องแตะสองครั้ง สำหรับลิงก์โต้ตอบใน textView ที่แก้ไขได้ ให้ใช้ gesture recognizers
SFSafariViewController เป็นวิธีมาตรฐานในการเปิดลิงก์ในแอปพลิเคชัน iOS ในเมธอด shouldInteractWithURL ให้สร้าง SFSafariViewController พร้อม URL และแสดงเป็นโมดัลหรือผ่าน navigationController สำหรับลิงก์สากลให้ใช้ UIApplication.shared.open(url, options) — แต่สิ่งนี้จะออกจากแอปพลิเคชัน เพื่อป้องกันการเปิดลิงก์ให้คืนค่า false ใน shouldInteractWithURL และจัดการ URL ด้วยตัวเอง
NSTextContainer เป็นส่วนหนึ่งของสถาปัตยกรรม TextKit ที่กำหนดพื้นที่เรขาคณิตที่แสดงข้อความ UITextView แต่ละ UITextView มี textContainer มาตรฐานที่กำหนดค่าได้ผ่าน: textView.textContainerInset — ระยะขอบข้อความจากขอบ textView (UIEdgeInsets); textView.textContainer.lineFragmentPadding — การเว้นวรรคแนวนอนภายในแต่ละบรรทัด; textView.textContainer.maximumNumberOfLines — จำกัดจำนวนบรรทัด (0 = ไม่จำกัด); textView.textContainer.exclusionPaths — อาร์เรย์ของ UIBezierPath สำหรับพื้นที่ที่ข้อความควรเลี่ยง (รูปภาพ รูปทรงที่กำหนดเอง) ExclusionPaths อนุญาตให้มีเลย์เอาต์ที่ซับซ้อน: ข้อความเลี่ยงรูปโปรไฟล์ทรงกลม ไอคอน หรือรูปร่างใด ๆ สำหรับความสูงแบบไดนามิกของ UITextView ให้สังเกต contentSize ผ่าน KVO หรือตัวแทนและอัปเดตข้อจำกัด
// 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 เป็นคุณสมบัติของ UITextView ที่จดจำและทำให้ข้อมูลบางประเภทในข้อความโต้ตอบได้โดยอัตโนมัติ: .link (URL, อีเมล), .phoneNumber (หมายเลขโทรศัพท์), .address (ที่อยู่), .calendarEvent (วันที่และกิจกรรม), .flightNumber (หมายเลขเที่ยวบิน), .lookupSuggestion (คำแนะนำการค้นหา), .trackingNumber (หมายเลขติดตาม), .money (จำนวนเงิน), .shipmentTrackingNumber, .all (ทุกประเภท) Data Detector ทำงานกับ NSDataDetector และ URL โดยเน้นข้อมูลที่ตรวจพบโดยอัตโนมัติ ในการปรับแต่งการจัดการให้ตั้งค่า dataDetectorTypes และแทนที่ shouldInteractWithURL ในตัวแทน สำคัญ: dataDetectorTypes ทำงานเฉพาะสำหรับ URL และ phoneNumber โดยไม่ต้องตั้งค่าเพิ่มเติม — ประเภทอื่นอาจต้องใช้ iOS 16+ Data Detector ไม่ขัดแย้งกับคุณสมบัติ link ใน NSAttributedString — ทั้งสองกลไกทำงานควบคู่กัน สำหรับ UITextView แบบอ่านอย่างเดียวที่มีเนื้อหา (ข่าวสาร บทความ) ให้ตั้งค่า dataDetectorTypes = [.link, .phoneNumber]
มาดูตัวอย่างการใช้งาน UITextView ในแอปพลิเคชัน iOS สองตัวอย่าง ตัวอย่างแรก — มุมมองข้อความแบบอ่านอย่างเดียวพร้อมข้อความที่มีคุณสมบัติและตัวตรวจจับข้อมูลสำหรับแสดงเนื้อหา ตัวอย่างที่สอง — มุมมองข้อความที่แก้ไขได้พร้อมตัวยึดตำแหน่งขี้เกียจและความสูงแบบไดนามิกสำหรับฟอร์มความคิดเห็น ทั้งสองตัวอย่างใช้แนวทางปฏิบัติที่ดีที่สุดของ UIKit: โปรโตคอลตัวแทน Auto Layout และ TextKit
โหมดอ่านอย่างเดียวเปิดใช้งานโดยตั้งค่า isEditable = false และ isSelectable = true ซึ่งอนุญาตให้ผู้ใช้เลือกข้อความและแตะลิงก์ แต่ไม่สามารถแก้ไขได้ ตั้งค่า dataDetectorTypes สำหรับการตรวจจับลิงก์อัตโนมัติ backgroundColor = .clear สำหรับพื้นหลังโปร่งใส สำหรับเนื้อหายาวให้ใช้ attributedText กับ NSMutableParagraphStyle lineSpacing เพื่อความอ่านง่าย เพิ่มคุณสมบัติ link สำหรับลิงก์สำคัญภายในข้อความ
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
}
}
ตัวยึดตำแหน่งสำหรับ UITextView ไม่ได้อยู่ในตัว — ให้ใช้งานผ่าน UILabel เหนือ textView หรือผ่าน attributedText พร้อมการตรวจสอบ isEmpty วิธีง่าย ๆ: UILabel ที่มีข้อความสีเทาซึ่งซ่อนเมื่อเริ่มแก้ไข ใน textViewDidChange ให้ตรวจสอบการมีอยู่ของข้อความและซ่อน/แสดงตัวยึดตำแหน่ง สำหรับโซลูชันที่หรูหรากว่าให้ใช้คุณสมบัติ typingAttributes เพื่อตั้งค่าสีข้อความที่ป้อน ความสูงแบบไดนามิกของ textView ทำได้โดยการสังเกต contentSize และอัปเดตข้อจำกัด — ซึ่งเป็นรูปแบบมาตรฐานสำหรับแชทและฟอร์มความคิดเห็น
คำถามที่พบบ่อย
UITextView ไม่มีตัวยึดตำแหน่งในตัว วิธีมาตรฐาน: เพิ่ม UILabel ที่มีข้อความสีเทาเหนือ textView ใน textViewDidBeginEditing ให้ซ่อนป้ายกำกับ ใน textViewDidEndEditing ให้แสดงถ้าข้อความว่างเปล่า อีกทางเลือก: ใช้ attributedText ด้วยสีเทาเป็นตัวยึดตำแหน่งและรีเซ็ตเมื่อเริ่มแก้ไข สำหรับโซลูชันสำเร็จรูปให้ใช้ไลบรารี KMPlaceholderTextView หรือ ReactorKit
ตั้งค่า textContainerInset = .zero และ lineFragmentPadding = 0: textView.textContainerInset = UIEdgeInsets.zero; textView.textContainer.lineFragmentPadding = 0 เพิ่มเติม: textView.contentInset = .zero ซึ่งจะลบระยะขอบจากขอบ textView ถึงข้อความอย่างสมบูรณ์ โปรดทราบว่า lineFragmentPadding ค่าเริ่มต้นคือ 5
ตรวจสอบ: isSelectable = true (จำเป็นสำหรับการโต้ตอบ), dataDetectorTypes รวม .link, ตัวแทนไม่คืนค่า false ใน shouldInteractWithURL ถ้า textView.isEditable = true ลิงก์จะไม่พร้อมใช้งานสำหรับการแตะครั้งเดียว — ต้องแตะสองครั้ง สำหรับลิงก์โต้ตอบใน textView ที่แก้ไขได้ให้เพิ่ม UITapGestureRecognizer เหนือ textView และจัดการ URL ด้วยตนเอง
ตั้งค่า isScrollEnabled = true และเพิ่มข้อจำกัดความสูงด้วยค่าคงที่ ข้อความที่เกินความสูงจะเลื่อน สำหรับ UX ที่เหมาะสมที่สุดให้ใช้ maxHeight: สังเกต contentSize และตั้งค่าข้อจำกัดความสูงไม่เกิน maxHeight เมื่อเกิน maxHeight ให้เปิดใช้งาน scrollEnabled นี่เป็นรูปแบบมาตรฐานสำหรับฟิลด์ป้อนข้อความและความคิดเห็น
ใช้ NSTextAttachment: สร้างออบเจกต์ ตั้งค่ารูปภาพและ bounds จากนั้นสร้าง NSAttributedString จากสิ่งที่แนบและแทรกลงใน textView.attributedText ตัวอย่าง: let attachment = NSTextAttachment(); attachment.image = image; attachment.bounds = CGRect(x, y, width, height); let attributedString = NSAttributedString(attachment: attachment) รูปภาพจะแสดงแบบอินไลน์ในข้อความและเลื่อนไปพร้อมกับมัน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม