UITextView هو مكون نص متعدد الأسطر في UIKit، يرث من UIScrollView ويدعم تحرير النص المنسق مع تضمين الروابط والصور. وفقاً لوثائق Apple Developer (2025)، يوفر UITextView بروتوكول UITextViewDelegate مع استدعاءات 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 أحادي السطر مع دعم placeholder وعرض يسار/يمين وزر مسح. UITextView هو UIScrollView متعدد الأسطر مع نظام TextKit كامل للنص المنسق. لا يدعم UITextField NSAttributedString على مستوى العرض (فقط عبر attributedText مع قيود)، بينما يتعامل UITextView مع NSAttributedString بالكامل بخطوط مختلفة وأنماط فقرة وصور مضمنة. يحتوي UITextField على زر مسح مدمج (clearButtonMode)، بينما لا يحتوي UITextView على ذلك — يلزم تنفيذ مخصص. يتغير حجم UITextField تلقائياً ليتناسب مع المحتوى (intrinsicContentSize)، ويتطلب UITextView إدارة يدوية للارتفاع عبر مراقبة contentSize أو قيود Auto Layout. يدعم UITextView dataDetectorTypes (الكشف التلقائي عن الروابط)، ويدعمه UITextField منذ iOS 16+. لإدخال النص متعدد الأسطر (التعليقات والملاحظات والرسائل) اختر UITextView. لحقول النموذج (الاسم والبريد الإلكتروني وكلمة المرور) استخدم UITextField.
| الميزة | UITextView | UITextField |
|---|---|---|
| الأسطر | متعدد الأسطر | أحادي السطر |
| Placeholder | لا (مخصص) | مدمج |
| سمات النص | 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. الأول — عرض نص للقراءة فقط مع نص منسق وكاشف بيانات لعرض المحتوى. الثاني — عرض نص قابل للتحرير مع placeholder كسول وارتفاع ديناميكي لنموذج تعليق. يستخدم كلا المثالين أفضل ممارسات 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
}
}
Placeholder لـ UITextView غير مدمج — قم بتنفيذه عبر UILabel فوق textView أو عبر attributedText مع فحص isEmpty. نهج بسيط: UILabel بنص رمادي يختفي عند بدء التحرير. في textViewDidChange تحقق من وجود النص وأخفِ/أظهر placeholder. لحل أكثر أناقة استخدم خاصية typingAttributes لتعيين لون نص الإدخال. يتم تحقيق الارتفاع الديناميكي للنص عبر مراقبة contentSize وتحديث القيود — هذا نمط قياسي للدردشات ونماذج التعليقات.
الأسئلة الشائعة
UITextView ليس لديه placeholder مدمج. الحل القياسي: أضف UILabel بنص رمادي فوق textView. في textViewDidBeginEditing أخفِ التسمية، في textViewDidEndEditing أظهرها إذا كان النص فارغاً. بدلاً من ذلك: استخدم attributedText بلون رمادي كـ placeholder وأعد تعيينه عند بدء التحرير. للحل الجاهز استخدم مكتبة 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 وأضف قيد ارتفاع بقيمة ثابتة. النص الذي يتجاوز الارتفاع سيتمرر. لتجربة مستخدم مثالية استخدم 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. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا