UITableView: مبانی، لیست‌ها و سلول‌ها در برنامه‌های iOS

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

UITableView — مؤلفه اصلی UIKit برای نمایش لیست‌های عمودی در iOS است. هر سطر جدول یک سلول UITableViewCell با سبک‌های از پیش تعیین‌شده است. مبانی UITableView را توضیح می‌دهیم: DataSource، delegate، استفاده مجدد از سلول‌ها و عملکرد جداول در برنامه‌های iOS. طبق داده‌های Apple (Human Interface Guidelines، 2026)، UITableView همچنان پرکاربردترین مؤلفه UI در iOS است — 85% از برنامه‌های top-100 App Store حداقل یک جدول دارند. برای چیدمان‌های پیچیده مقاله UICollectionView را بخوانید.

نکات اصلی

  • UITableView — مؤلفه UIKit برای لیست‌های عمودی، قابل دسترس از iOS 2.0 (iPhone OS 2).
  • UITableViewCell — سلول جدول با چهار سبک داخلی و قابلیت شخصی‌سازی.
  • UITableViewDataSource — پروتکلی که تعداد سطرها و سلول‌ها را برای هر بخش تعیین می‌کند.
  • UITableViewDelegate — پروتکل برای مدیریت انتخاب، ویرایش و تنظیم ارتفاع سطرها.
  • UITableViewDiffableDataSource — DataSource مدرن (iOS 13+) با محاسبه خودکار تغییرات.

UITableView در iOS چیست؟

UITableView — کلاسی از فریمورک UIKit است که برای نمایش لیست قابل پیمایش از سطرها در یک ستون طراحی شده است. هر سطر توسط یک شی UITableViewCell نمایش داده می‌شود. UITableView در iPhone OS 2 (2008) ظاهر شد و از آن زمان مؤلفه اصلی برای لیست‌ها در iOS باقی مانده است. معماری UITableView بر اساس الگوی MVC ساخته شده است: داده‌ها توسط DataSource، ظاهر و رفتار توسط Delegate، و نمای توسط خود جدول مدیریت می‌شود.

دو سبک جدول: .plain (لیست پیوسته با بخش‌های اختیاری) و .grouped (بخش‌های گروه‌بندی‌شده با گوشه‌های گرد و فاصله). از iOS 13 به بعد .insetGrouped اضافه شده است — سبک grouped با فاصله از لبه‌ها، که در برنامه‌های Settings و Health استفاده می‌شود. انتخاب سبک بر ظاهر پیش‌فرض تأثیر می‌گذارد: جداول plain هدر بخش را هنگام پیمایش به بالا می‌چسبانند (sticky header)، grouped — نمی‌چسبانند.

Self-Sizing Cells

از iOS 8 به بعد، UITableView از سلول‌های با اندازه خودکار (self-sizing cells) پشتیبانی می‌کند. برای فعال‌سازی tableView.estimatedRowHeight (مثلاً 80) و tableView.rowHeight = UITableView.automaticDimension را تنظیم کنید. جدول به طور خودکار ارتفاع سطر را بر اساس محدودیت‌های Auto Layout داخل سلول محاسبه می‌کند. Self-sizing عملکرد را برای سلول‌های پیچیده کاهش می‌دهد — برای جداول با 500+ عنصر از ارتفاع ثابت (rowHeight) استفاده کنید.

سبک‌های UITableViewCell: basic، subtitle، value1، value2

UITableViewCell چهار سبک داخلی ارائه می‌دهد که 80% سناریوها را بدون نیاز به ایجاد سلول سفارشی پوشش می‌دهد. هر سبک از ترکیبی از textLabel، detailTextLabel و imageView تشکیل شده است. انتخاب سبک هنگام مقداردهی اولیه سلول از طریق init(style:reuseIdentifier:) تنظیم می‌شود.

سبکtextLabeldetailTextLabelimageViewمثال
.default (.basic)چپ، پررنگندارداختیاریمنو، لیست موارد
.subtitleچپ، پررنگزیر textLabel، خاکستریاختیاریمخاطبین، لیست‌های پخش
.value1چپ، پررنگراست، خاکسترینداردتنظیمات با مقادیر
.value2راست، آبیچپ، خاکستریندارددفترچه تلفن (سبک iOS 6)
swift
// ایجاد جدول با سلول‌های سفارشی
class ContactListController: UIViewController {

    private let tableView = UITableView(frame: .zero, style: .insetGrouped)

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = 60
        view.addSubview(tableView)

        // Auto Layout
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension ContactListController: UITableViewDataSource {

    func tableView(_ tableView: UITableView,
                         numberOfRowsInSection section: Int) -> Int {
        contacts.count
    }

    func tableView(_ tableView: UITableView,
                         cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        let contact = contacts[indexPath.row]

        var content = cell.defaultContentConfiguration()
        content.text = contact.name
        content.secondaryText = contact.phone
        content.image = UIImage(systemName: "person.circle")
        cell.contentConfiguration = content

        return cell
    }
}

UIContentConfiguration (iOS 14+) — روش مدرن پیکربندی سلول‌ها از طریق contentConfiguration به جای دسترسی مستقیم به textLabel/detailTextLabel. از cell.defaultContentConfiguration() استفاده کنید یا یک UIContentConfiguration سفارشی ایجاد کنید. مزایا: پشتیبانی از نوع پویا، تم تاریک و VoiceOut از پیش فراهم شده است. Apple (WWDC 2024) contentConfiguration را برای همه UITableViewهای جدید توصیه می‌کند.

DataSource و Delegate: روش‌های اجباری

UITableViewDataSource — پروتکلی که داده‌های جدول را فراهم می‌کند. روش‌های اجباری: tableView(_:numberOfRowsInSection:) و tableView(_:cellForRowAt:). بدون آنها جدول نمی‌تواند هیچ سطری را نمایش دهد. UITableViewDelegate — پروتکل برای مدیریت ظاهر و رفتار: ارتفاع سطرها، انتخاب، ویرایش، contextual actions.

swift
// مثال با چندین بخش و ویرایش
extension ContactListController: UITableViewDelegate {

    // ارتفاع سفارشی برای هر سطر
    func tableView(_ tableView: UITableView,
                         heightForRowAt indexPath: IndexPath) -> CGFloat {
        80
    }

    // مدیریت انتخاب
    func tableView(_ tableView: UITableView,
                         didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let contact = contacts[indexPath.row]
        showDetail(for: contact)
    }

    // کشیدن برای حذف
    func tableView(_ tableView: UITableView,
                         trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
        -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "حذف") {
            [weak self] _, _, completion in
            self?.contacts.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
            completion(true)
        }
        return UISwipeActionsConfiguration(actions: [deleteAction])
    }
}

عملکرد Delegate: روش‌های اندازه (heightForRowAt) برای هر سطر قابل مشاهده در هر پیمایش فراخوانی می‌شوند. برای ارتفاع یکسان، rowHeight را مستقیماً تنظیم کنید — این 10 برابر سریع‌تر از heightForRowAt است. برای سلول‌های self-sizing، estimatedRowHeight و automaticDimension را تنظیم کنید — جدول heightForRowAt را فقط برای سطرهای قابل مشاهده فراخوانی می‌کند و برای بقیه از estimatedRowHeight استفاده می‌کند.

استفاده مجدد از سلول‌ها: صف reuse و register

صف reuse — استخری از سلول‌های قابل استفاده مجدد UITableView. هنگامی که یک سلول از صفحه خارج می‌شود، حذف نمی‌شود بلکه در صف قرار می‌گیرد. سلول‌های جدید از صفر ایجاد نمی‌شوند — سیستم dequeueReusableCell(withIdentifier:for:) را فراخوانی می‌کند که سلولی را از صف برمی‌گرداند یا اگر صف خالی باشد سلول جدیدی ایجاد می‌کند. این مکانیسم کلیدی عملکرد UITableView است.

swift
// سلول سفارشی با کش کردن
class ContactCell: UITableViewCell {

    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let subtitleLabel = UILabel()

    override init(style: UITableViewCell.Style, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        contentView.addSubview(avatarView)
        contentView.addSubview(nameLabel)
        contentView.addSubview(subtitleLabel)

        // تنظیم محدودیت‌های Auto Layout
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        // ... محدودیت‌ها ...
    }

    func configure(with contact: Contact) {
        nameLabel.text = contact.name
        subtitleLabel.text = contact.phone
        // بارگذاری ناهمگام آواتار
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        avatarView.image = nil
        nameLabel.text = nil
        subtitleLabel.text = nil
    }
}

// ثبت و استفاده
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")

// در cellForRowAt:
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactCell
cell.configure(with: contacts[indexPath.row])
return cell

prepareForReuse() — روشی که قبل از قرار دادن سلول در صف reuse فراخوانی می‌شود. تمام وضعیت سلول را بازنشانی کنید: متن، تصاویر، انیمیشن‌ها، بارگذاری‌ها. بدون prepareForReuse، سلول استفاده مجدد شده ممکن است داده‌های قدیمی را نشان دهد: تصویر قبلی برای کسری از ثانیه قابل مشاهده خواهد بود تا زمانی که تصویر جدید بارگذاری شود. Apple (WWDC 2023) توصیه می‌کند عملیات‌های ناهمگام را از طریق URLSessionTask.cancel() در prepareForReuse پاک کنید.

بخش‌ها، header و footer: گروه‌بندی داده‌ها

بخش‌ها در UITableView سطرهای مرتبط منطقی را گروه‌بندی می‌کنند. هر بخش می‌تواند header (عنوان) و footer (پاورقی) داشته باشد. تعداد بخش‌ها توسط روش numberOfSections(in:) تعیین می‌شود (پیش‌فرض 1). برای سبک‌های grouped و insetGrouped، بخش‌ها به صورت بصری با فاصله و گوشه‌های گرد جدا می‌شوند.

روش DataSourceتوضیحمقدار بازگشتی
numberOfSectionsتعداد بخش‌ها (پیش‌فرض 1)Int
numberOfRowsInSectionتعداد سطرها در بخشInt
titleForHeaderInSectionمتن عنوان بخشString?
titleForFooterInSectionمتن پاورقی بخشString?
viewForHeaderInSectionنمای سفارشی برای headerUIView?
heightForHeaderInSectionارتفاع headerCGFloat

نمایه‌سازی: برای پیمایش سریع بین بخش‌ها، یک نمایه اضافه کنید — آرایه‌ای از رشته‌ها که در سمت راست نمایش داده می‌شوند. sectionIndexTitles(for:) را پیاده‌سازی کنید — آرایه‌ای از حروف اول هر بخش را برمی‌گرداند. برای 26+ بخش، این به طور بحرانی UX را بهبود می‌بخشد. در iOS 15+، جداول از sectionHeaderTopPadding پشتیبانی می‌کنند — فاصله بین نوار وضعیت و اولین header که می‌تواند برای چیدمان فشرده به 0 بازنشانی شود.

عملکرد UITableView: بهترین روش‌ها

عملکرد UITableView برای برنامه‌های با لیست‌های طولانی حیاتی است. مشکلات اصلی: پیمایش آهسته (jank) به دلیل عملیات سنگین در cellForRowAt، فراخوانی‌های مکرر heightForRowAt، عدم وجود prefetching. Apple (WWDC 2025) پنج روش کلیدی را برای جداول با 1000+ عنصر برجسته می‌کند.

  • Flat-height — برای همه سلول‌ها به جای heightForRowAt از rowHeight ثابت استفاده کنید. تفاوت: 60 FPS vs 30 FPS برای جدول 500 سطری.
  • Prefetching — UITableViewDataSourcePrefetching را برای بارگذاری ناهمگام داده‌ها (تصاویر، API) قبل از ظاهر شدن سلول روی صفحه پیاده‌سازی کنید.
  • Subview کمتر — هر subview در contentView زمان رندر را افزایش می‌دهد. برای گرافیک ساده (جداکننده‌ها، آیکون‌ها) به جای UIImageView از draw() استفاده کنید.
  • عملیات سنگین در پس‌زمینه — قالب‌بندی تاریخ، بومی‌سازی، محاسبات را در مدل انجام دهید، نه در cellForRowAt. از NSCache برای نتایج استفاده کنید.
  • DiffableDataSource — reloadData() را با apply(snapshot) جایگزین کنید — انیمیشن به صورت رایگان انجام می‌شود، جدول به طور کامل بارگذاری مجدد نمی‌شود.
swift
// بهینه‌سازی با prefetching
extension ContactListController: UITableViewDataSourcePrefetching {

    func tableView(_ tableView: UITableView,
                         prefetchRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.prefetch(url: contact.avatarUrl)
        }
    }

    func tableView(_ tableView: UITableView,
                         cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
        for indexPath in indexPaths {
            let contact = contacts[indexPath.row]
            ImageCache.shared.cancelPrefetch(url: contact.avatarUrl)
        }
    }
}

// استفاده از DiffableDataSource برای به‌روزرسانی‌های روان
class ModernTableController: UIViewController {

    private var dataSource: UITableViewDiffableDataSource<Section, Contact>!

    func applyContacts(_ contacts: [Contact]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Contact>()
        snapshot.appendSections([.main])
        snapshot.appendItems(contacts)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

نتیجه عملی: استفاده از این بهینه‌سازی‌ها FPS پیمایش را از 30 به 60 در دستگاه‌های iPhone 12 و بالاتر افزایش می‌دهد (طبق آزمایش‌های Apple، 2025). در پروژه‌های IT Sectr، ما از DiffableDataSource برای همه جداول جدید استفاده می‌کنیم و برای جداول با تصاویر (آواتارها، پیش‌نمایش‌ها، آیکون‌های برنامه) prefetching اضافه می‌کنیم.

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

UITableView پیمایش نمی‌شود — چه کار کنیم؟

بررسی کنید: (1) contentSize > frame.size — جدول باید محتوای بیشتری از ارتفاع خود داشته باشد؛ (2) جدول داخل UIScrollView دیگری که حرکات را رهگیری می‌کند نیست؛ (3) isScrollEnabled = true؛ (4) numberOfRowsInSection تعداد صحیح را برمی‌گرداند. مشکل معمول — جدول با heightForRowAt = UITableView.automaticDimension بدون estimatedRowHeight که منجر به محاسبه بی‌نهایت ارتفاع می‌شود.

چگونه UITableView را بدون چشمک‌زدن به‌روزرسانی کنیم؟

به جای reloadData() از performBatchUpdates یا DiffableDataSource استفاده کنید. reloadData() باعث بارگذاری مجدد کامل بدون انیمیشن می‌شود و چشمک‌زدن بصری ایجاد می‌کند. برای به‌روزرسانی نقطه‌ای از: reloadRows(at:with:)، insertRows، deleteRows استفاده کنید. تمام تغییرات داخل performBatchUpdates به طور همزمان انیمیشن می‌شوند. DiffableDataSource این کار را به طور خودکار بدون فراخوانی دستی انجام می‌دهد.

تفاوت بین UITableView و UICollectionView چیست؟

UITableView — لیست عمودی تک‌ستونی با سبک‌های سلول از پیش تعیین‌شده و ویرایش داخلی. UICollectionView — چیدمان انعطاف‌پذیر (گرید، لیست، کاروسل، آبشاری) با قرارگیری دلخواه عناصر. UITableView را برای لیست‌های ساده انتخاب کنید (تنظیمات، مخاطبین، پیام‌ها). UICollectionView را برای گالری‌ها، کاتالوگ‌ها، تابلوها و هر چیدمان غیرخطی انتخاب کنید.

چگونه سطرهای خالی زیر محتوا را در UITableView حذف کنیم؟

tableView.tableFooterView = UIView(frame: .zero) را تنظیم کنید. به طور پیش‌فرض، UITableView سطرهای خالی (جداکننده‌ها) را در زیر آخرین عنصر تا لبه پایینی نمایش می‌دهد. تنظیم یک UIView خالی در tableFooterView آنها را حذف می‌کند. برای سبک grouped این مورد نیاز نیست — جدول فقط سطرهای واقعی را با گوشه‌های گرد آخرین بخش نمایش می‌دهد.

چگونه UITableView با ارتفاع ثابت بسازیم؟

tableView.rowHeight = 80 و tableView.estimatedRowHeight = 0 را تنظیم کنید یا estimatedRowHeight را حذف کنید. با ارتفاع ثابت، UITableView heightForRowAt را فراخوانی نمی‌کند که رندر را برای لیست‌های با 100+ عنصر 5–10 برابر سریع‌تر می‌کند. برای سلول‌های با ارتفاع سفارشی (محتوا متفاوت) از self-sizing استفاده کنید: estimatedRowHeight + UITableView.automaticDimension.

خلاصه

  • UITableView — مؤلفه اصلی UIKit برای لیست‌های عمودی با معماری DataSource و Delegate.
  • UITableViewCell — 4 سبک داخلی (.default، .subtitle، .value1، .value2) و سفارشی از طریق contentConfiguration.
  • صف reuse — استخر سلول‌های قابل استفاده مجدد؛ register + dequeueReusableCell + prepareForReuse اجباری هستند.
  • DiffableDataSource (iOS 13+) — DataSource ایمن از نظر نوع با محاسبه خودکار تغییرات و انیمیشن.
  • عملکرد: rowHeight ثابت، prefetching، subview کمتر، عملیات سنگین در پس‌زمینه.
  • 85% از برنامه‌های iOS top-100 App Store از UITableView برای نمایش محتوا استفاده می‌کنند.
  • برای گریدها، کاروسل‌ها و چیدمان‌های پیچیده به جای UITableView از UICollectionView استفاده کنید.

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

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

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

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