NSTextAttachment: چیست و چگونه تصویر به متن در iOS اضافه کنیم

نویسنده: IT Sectr منتشر شده: 2026-07-25 زمان مطالعه: 9 دقیقه

NSTextAttachment کلاسی از فریم‌ورک UIKit است که امکان جاسازی تصاویر و سایر اشیاء چندرسانه‌ای را در متن فرمت‌شده از طریق NSAttributedString فراهم می‌کند. این کلاس در سطح TextKit و Core Text کار می‌کند و به توسعه‌دهندگان کنترل بر اندازه، موقعیت و رفتار پیوست‌ها در داخل جریان متن می‌دهد. بر اساس Apple Documentation (2025)، NSTextAttachment از تصاویر، PDF و کانتینرهای نمایش سفارشی پشتیبانی می‌کند و با UITextView و UILabel ادغام می‌شود. درک این API برای ایجاد Rich Text در برنامه‌های iOS ضروری است — از چت‌ها و ویرایشگرها گرفته تا فیدهای خبری با آیکون‌ها در زیرنویس‌ها.

نکات اصلی

  • NSTextAttachment — کلاس UIKit برای جاسازی رسانه در متن فرمت‌شده iOS.
  • تصویر از طریق NSAttributedString با کلید NSAttachmentAttributeName اضافه می‌شود.
  • ویژگی bounds اندازه و جابجایی عمودی پیوست را کنترل می‌کند.
  • NSTextAttachment از تصاویر، PDF و فایل‌های سفارشی از طریق کانتینر داده پشتیبانی می‌کند.
  • API با UITextView، UILabel و TextKit برای چیدمان انعطاف‌پذیر متن ادغام می‌شود.

NSTextAttachment چیست؟

NSTextAttachment کلاسی از فریم‌ورک UIKit است که امکان جاسازی اشیاء چندرسانه‌ای را مستقیماً در محتوای متنی NSAttributedString فراهم می‌کند. برخلاف UIImageView جداگانه که در کنار متن قرار می‌گیرد، NSTextAttachment تصویر را به بخشی از جریان متن تبدیل می‌کند: تصویر همراه با خطوط منتقل می‌شود، در ترازبندی شرکت می‌کند و مانند یک کاراکتر معمولی فضا اشغال می‌کند.

این کلاس بخشی از معماری TextKit است که رندرینگ متن را در iOS و macOS مدیریت می‌کند. TextKit متن را به گلیف‌ها تقسیم می‌کند، کرنینگ، لیگاتور و فاصله بین خطوط را در نظر می‌گیرد — و NSTextAttachment به این خط لوله به عنوان یک کاراکتر ویژه می‌پیوندد که نه به صورت حرف، بلکه به عنوان یک شیء گرافیکی رندر می‌شود.

هر نمونه NSTextAttachment شامل ویژگی‌های contents (داده‌های فایل)، fileType (نوع UTI محتوا) و bounds (مستطیل بر حسب points که اندازه و موقعیت را تعیین می‌کند) است. طبق Apple Human Interface Guidelines، bounds به طور پیش‌فرض برابر با اندازه تصویر است، اما می‌تواند برای تطبیق دقیق با فونت تغییر کند.

زمانی که تصویر باید مانند بخشی از متن رفتار کند — در چت‌ها، فیدهای خبری، ویرایشگرهای متن و فرم‌های دارای آیکون در فیلدهای ورودی — از NSTextAttachment به جای UIImageView جداگانه استفاده کنید.

چگونه تصویر به متن از طریق NSTextAttachment اضافه کنیم

سناریوی پایه کار با NSTextAttachment از سه مرحله تشکیل شده است: ایجاد نمونه، تنظیم تصویر و پیوست به NSAttributedString. بیایید فرآیند را با مثال Swift بررسی کنیم.

swift
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "icon-star")

let attachmentString = NSAttributedString(attachment: attachment)

let text = NSMutableAttributedString(string: "Rating: ")
text.append(attachmentString)

let label = UILabel()
label.attributedText = text

پس از فراخوانی NSAttributedString(attachment:)، شیء NSTextAttachment به ویژگی NSAttachmentAttributeName تبدیل می‌شود که به یک کاراکتر جایگزین ویژه (object replacement character، کد 0xFFFC) متصل می‌شود. این کاراکتر به صورت حرف نمایش داده نمی‌شود — به جای آن تصویر از ویژگی image رسم می‌شود.

اگر تصویر باید در وسط خط متن قرار گیرد (مثلاً آیکون بعد از کلمه)، کافیست attachmentString را در موقعیت مورد نظر NSMutableAttributedString وارد کنید. TextKit به طور خودکار ارتفاع خط را در نظر گرفته و تصویر را روی خط پایه تراز می‌کند.

مهم: ویژگی image فقط در iOS (از iOS 7 به بعد) در دسترس است. در macOS از ویژگی contents با NSImage استفاده کنید. برای سازگاری با نسخه‌های قدیمی، همیشه image را مستقیماً تنظیم کنید، نه از طریق کانتینر داده.

تنظیم bounds و موقعیت پیوست

به طور پیش‌فرض NSTextAttachment تصویر را در اندازه اصلی آن بر حسب points نمایش می‌دهد. در عمل تقریباً همیشه نیاز به تنظیم اندازه و موقعیت عمودی وجود دارد — برای این کار از ویژگی bounds استفاده می‌شود.

ساختار CGRect bounds شامل origin (جابجایی در X و Y) و size (عرض و ارتفاع) است. جابجایی Y به ویژه مهم است: مقدار منفی تصویر را زیر خط پایه قرار می‌دهد، مقدار مثبت آن را بالا می‌برد. سناریوی معمول — تراز کردن آیکون در مرکز خط متن.

swift
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "icon-star")

let font = UIFont.systemFont(ofSize: 16)
let fontCapHeight = font.capHeight
let imageSize = CGSize(width: 18, height: 18)

attachment.bounds = CGRect(
    x: 0,
    y: (fontCapHeight - imageSize.height) / 2,
    width: imageSize.width,
    height: imageSize.height
)

محاسبه تراز از capHeight فونت استفاده می‌کند — ارتفاع حرف بزرگ، نه ارتفاع کامل خط. این تضمین می‌کند که آیکون از نظر بصری با مرز بالایی متن منطبق است، نه اینکه بین baseline و ascender سرگردان باشد. اندازه تصویر در مثال 18×18 pt است که برای آیکون‌ها در متن UI معمول است.

اگر تصویر باید بزرگتر از خط متن باشد (مثلاً پیش‌نمایش عکس در چت)، TextKit به طور خودکار فاصله بین خطوط را برای خط جاری افزایش می‌دهد. در این حالت bounds در اندازه طبیعی تنظیم می‌شود و جابجایی Y صفر است.

PDF و پیوست‌های سفارشی

NSTextAttachment نه تنها از PNG و JPEG، بلکه از اسناد PDF و همچنین فایل‌های دلخواه از طریق ویژگی contents پشتیبانی می‌کند. این آن را به ابزاری جهانی برای Rich Text در برنامه‌های iOS تبدیل می‌کند.

هنگام تنظیم تصویر PDF در attachment.image، سیستم به طور خودکار اولین صفحه سند را رندر می‌کند. با این حال برای نمایش دقیق، مستقیماً از داده از طریق مقداردهنده اولیه با data و نوع UTI استفاده کنید:

swift
guard let pdfData = Bundle.main.url(forResource: "document",
                                  withExtension: "pdf") else { return }

let fileData = Data(contentsOf: pdfData)
let attachment = NSTextAttachment(data: fileData,
                                       ofType: "com.adobe.pdf")

attachment.bounds = CGRect(x: 0, y: -4,
                           width: 24, height: 24)

let pdfString = NSAttributedString(attachment: attachment)

پارامتر ofType یک رشته UTI (Uniform Type Identifier) دریافت می‌کند. انواع استاندارد: public.image (هر تصویری)، public.jpeg، public.png، com.adobe.pdf. هنگام تعیین UTI صحیح، سیستم روش رندر مناسب را انتخاب می‌کند — برای PDF این CGPDFDocument است، برای تصاویر — CGImageSource.

برای انواع فایل سفارشی (مثلاً گرافیک برداری با فرمت SVG) به زیرکلاسی از NSTextAttachment نیاز است که متد image(forBounds:textContainer:characterIndex:) را بازنویسی کند. اگر رندر استاندارد مناسب نیست، nil برگردانید و رسم را از طریق Core Graphics در متد draw پیاده‌سازی کنید.

NSTextAttachment در UITextView و تفویض

NSTextAttachment هم در UILabel و هم در UITextView به درستی کار می‌کند. با این حال UITextView امکانات بیشتری فراهم می‌کند: تعامل با پیوست‌ها (لمس آیکون)، ویرایش متن همراه با تصاویر و پشتیبانی از NSAttachmentBehavior.

برای مدیریت لمس NSTextAttachment در UITextView از نماینده UITextViewDelegate و متد textView(_:shouldInteractWith:in:interaction:) استفاده می‌شود. این متد زمانی فراخوانی می‌شود که کاربر پیوست را لمس می‌کند و امکان انتقال به صفحه، باز کردن پاپ‌آپ یا پخش انیمیشن را فراهم می‌کند.

swift
func textView(_ textView: UITextView,
                shouldInteractWith attachment: NSTextAttachment,
                in characterRange: NSRange,
                interaction: UITextItemInteraction) -> Bool {
    if interaction == .preview {
        return false
    }
    // باز کردن صفحه جزئیات
    showImageDetail(attachment.image)
    return false
}

متد انواع تعامل را از طریق پارامتر interaction متمایز می‌کند: .preview (3D Touch / Haptic Touch)، .default (لمس معمولی) و .presentActions (منوی زمینه). برای هر نوع می‌توان رفتار خود را تنظیم کرد یا با برگرداندن false آن را ممنوع کرد.

هنگام ویرایش UITextView با NSTextAttachment مهم است به خاطر داشته باشید: حذف کاراکتر جایگزین (0xFFFC) پیوست را نیز حذف می‌کند. کاربر تصویر را به عنوان یک عنصر واحد می‌بیند — آن به طور کامل انتخاب می‌شود، نه پیکسل به پیکسل. برای پشتیبانی از کشیدن و رها کردن پیوست‌ها در iOS 15+ از NSTextAttachmentViewProvider استفاده کنید.

خطاهای رایج هنگام کار با NSTextAttachment

اولین خطای رایج — نادیده گرفتن bounds. توسعه‌دهندگان اغلب به اندازه اصلی تصویر تکیه می‌کنند که منجر به آیکون‌های غول‌پیکر در متن یا برعکس، تصاویری که به سختی دیده می‌شوند، می‌گردد. همیشه bounds را با در نظر گرفتن فونت و زمینه به صراحت تنظیم کنید.

دومین خطا — جابجایی نادرست Y. مقدار مثبت در bounds.origin.y تصویر را پایین می‌آورد (در سیستم مختصات UIKit محور Y به سمت پایین است — این غیرمنطقی به نظر می‌رسد، اما Core Graphics اینگونه کار می‌کند). برای تراز کردن در مرکز خط از فرمول با capHeight فونت استفاده کنید، همانطور که در بخش 3 نشان داده شده است.

سومین مشکل — از دست رفتن تصویر هنگام تغییر traitCollection. وقتی کاربر Dark Mode یا Dynamic Type را تغییر می‌دهد، اندازه فونت ممکن است تغییر کند، اما bounds ثابت می‌ماند. راه‌حل — محاسبه پویای bounds در متد layoutSubviews یا از طریق KVO روی font.

چهارمین خطای رایج — استفاده از NSTextAttachment در UILabel با numberOfLines > 1. در حالت چندخطی با عرض محدود، TextKit به درستی خطوط را همراه با تصویر منتقل می‌کند. مشکل زمانی رخ می‌دهد که ارتفاع پیوست از ارتفاع خط فراتر می‌رود — خطوط مجاور روی هم قرار می‌گیرند. راه‌حل — افزایش lineSpacing از طریق NSMutableParagraphStyle.

سوالات متداول

تفاوت NSTextAttachment با UIImageView در متن چیست؟

NSTextAttachment تصویر را بخشی از جریان متن می‌کند: همراه با متن منتقل می‌شود، تراز و مقیاس‌بندی می‌شود. UIImageView به مختصات superview وابسته است و در چیدمان متن شرکت نمی‌کند.

آیا می‌توان NSTextAttachment را به UILabel اضافه کرد؟

بله، اگر ویژگی attributedText را به جای text تنظیم کنید. UILabel به درستی NSTextAttachment را نمایش می‌دهد، اما از تعامل پشتیبانی نمی‌کند. برای لمس پیوست از UITextView استفاده کنید.

چگونه اندازه تصویر را پس از ایجاد attachment تغییر دهیم؟

ویژگی bounds نمونه موجود NSTextAttachment را تغییر دهید. متن به طور خودکار با اندازه‌های جدید بدون نیاز به ایجاد NSAttributedString جدید رندر می‌شود.

آیا NSTextAttachment از تصاویر متحرک پشتیبانی می‌کند؟

NSTextAttachment از تصاویر ایستا پشتیبانی می‌کند. برای GIF و فرمت‌های متحرک نیاز به پیاده‌سازی سفارشی از طریق NSTextAttachmentViewProvider در iOS 15+ یا از طریق CADisplayLink است.

چگونه NSTextAttachment را به صورت برنامه‌نویسی به متن اضافه کنیم؟

یک نمونه NSTextAttachment ایجاد کنید، image یا data را تنظیم کنید، bounds را تعیین کنید، در NSAttributedString(attachment:) بپیچید و از طریق متد insert(_:at:) در NSMutableAttributedString وارد کنید.

خلاصه

  • NSTextAttachment — کلاس UIKit برای جاسازی تصاویر، PDF و سایر رسانه‌ها در متن فرمت‌شده.
  • تصویر از طریق NSAttributedString(attachment:) اضافه می‌شود و بخشی از جریان متن می‌گردد.
  • ویژگی bounds اندازه و موقعیت را کنترل می‌کند: همیشه آن را با در نظر گرفتن فونت به صراحت تنظیم کنید.
  • برای تراز کردن آیکون در مرکز خط از فرمول با capHeight فونت جاری استفاده کنید.
  • پیوست‌های PDF از طریق مقداردهنده اولیه با data و نوع UTI صحیح پشتیبانی می‌شوند.
  • برای مدیریت لمس پیوست از UITextViewDelegate و متد shouldInteractWith استفاده کنید.
  • هنگام تغییر Dynamic Type bounds را به صورت پویا دوباره محاسبه کنید تا از هم‌پوشانی خطوط جلوگیری شود.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید