UITableView: temel bilgiler, iOS uygulamalarında listeler ve hücreler

Yazar: IT Sectr Yayınlanma: 2026-02-23 Okuma süresi: 9 dk

UITableView — iOS'ta dikey listeleri görüntülemek için UIKit'in ana bileşenidir. Tablonun her satırı, önceden tanımlanmış stillere sahip bir UITableViewCell'dir. UITableView'in temellerini açıklıyoruz: DataSource, delegate, hücre yeniden kullanımı ve iOS uygulamalarında tablo performansı. Apple'a (Human Interface Guidelines, 2026) göre UITableView, iOS'ta en çok kullanılan UI bileşeni olmaya devam ediyor — App Store ilk 100'deki uygulamaların %85'i en az bir tablo içeriyor. Karmaşık düzenler için UICollectionView hakkındaki makaleyi okuyun.

Önemli Noktalar

  • UITableView — dikey listeler için UIKit bileşeni, iOS 2.0'dan (iPhone OS 2) beri kullanılabilir.
  • UITableViewCell — dört yerleşik stile ve özelleştirme seçeneklerine sahip bir tablo hücresi.
  • UITableViewDataSource — her bölüm için satır ve hücre sayısını tanımlayan bir protokol.
  • UITableViewDelegate — seçim, düzenleme ve satır yüksekliği yapılandırmasını işleyen bir protokol.
  • UITableViewDiffableDataSource — otomatik değişiklik hesaplamasına sahip modern DataSource (iOS 13+).

iOS'ta UITableView nedir?

UITableView, tek bir sütunda kaydırılabilir satır listesi görüntülemek için tasarlanmış UIKit framework'ünün bir sınıfıdır. Her satır bir UITableViewCell nesnesi ile temsil edilir. UITableView ilk olarak iPhone OS 2'de (2008) ortaya çıktı ve o zamandan beri iOS'ta listeler için ana bileşen olarak kaldı. UITableView'in mimarisi MVC desenine dayanır: veriler DataSource tarafından, görünüm ve davranış Delegate tarafından ve sunum tablonun kendisi tarafından yönetilir.

İki tablo stili: .plain (isteğe bağlı bölümlerle sürekli liste) ve .grouped (yuvarlak köşeli ve kenar boşluklu gruplanmış bölümler). iOS 13, .insetGrouped stilini ekledi — kenarlarda boşlukları olan gruplandırılmış stil, Ayarlar ve Sağlık uygulamalarında kullanılır. Stil seçimi varsayılan görünümü etkiler: plain tablolar kaydırma sırasında bölüm başlıklarını üste sabitler (yapışkan başlık), grouped tablolar sabitlemez.

Kendinden Boyutlandıran Hücreler

iOS 8'den itibaren UITableView, kendinden boyutlandıran hücreleri (self-sizing cells) destekler. Etkinleştirmek için tableView.estimatedRowHeight (örneğin, 80) ve tableView.rowHeight = UITableView.automaticDimension ayarlayın. Tablo, hücre içindeki Auto Layout kısıtlamalarına dayanarak satır yüksekliğini otomatik olarak hesaplar. Kendinden boyutlandırma karmaşık hücreler için performansı düşürür — 500+ öğeli tablolar için sabit yükseklik (rowHeight) kullanın.

UITableViewCell Stilleri: Basic, Subtitle, Value1, Value2

UITableViewCell, özel bir hücre oluşturmaya gerek kalmadan senaryoların %80'ini kapsayan dört yerleşik stil sağlar. Her stil, textLabel, detailTextLabel ve imageView'in bir kombinasyonundan oluşur. Stil, init(style:reuseIdentifier:) ile hücre başlatılırken seçilir.

StiltextLabeldetailTextLabelimageViewÖrnek
.default (.basic)Sol, kalınYokİsteğe bağlıMenü, öğe listesi
.subtitleSol, kalıntextLabel altında, griİsteğe bağlıKişiler, çalma listeleri
.value1Sol, kalınSağ, griYokDeğerli ayarlar
.value2Sağ, maviSol, griYokTelefon rehberi (iOS 6 stili)
swift
// Özel hücrelerle tablo oluşturma
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+) — textLabel/detailTextLabel'e doğrudan erişmek yerine contentConfiguration aracılığıyla hücreleri yapılandırmanın modern bir yoludur. cell.defaultContentConfiguration() kullanın veya özel bir UIContentConfiguration oluşturun. Avantajları: Dynamic Type, karanlık mod ve VoiceOver için yerleşik destek. Apple (WWDC 2024) tüm yeni UITableView'ler için contentConfiguration önermektedir.

DataSource ve Delegate: zorunlu yöntemler

UITableViewDataSource — tabloya veri sağlayan bir protokol. Zorunlu yöntemler: tableView(_:numberOfRowsInSection:) ve tableView(_:cellForRowAt:). Bunlar olmadan tablo tek bir satır bile görüntüleyemez. UITableViewDelegate — görünüm ve davranışı yönetmek için protokol: satır yüksekliği, seçim, düzenleme, bağlamsal eylemler.

swift
// Birden çok bölüm ve düzenleme örneği
extension ContactListController: UITableViewDelegate {

    // Her satır için özel yükseklik
    func tableView(_ tableView: UITableView,
                         heightForRowAt indexPath: IndexPath) -> CGFloat {
        80
    }

    // Seçim işleme
    func tableView(_ tableView: UITableView,
                         didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let contact = contacts[indexPath.row]
        showDetail(for: contact)
    }

    // Swipe-to-delete
    func tableView(_ tableView: UITableView,
                         trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
        -> UISwipeActionsConfiguration? {
        let deleteAction = UIContextualAction(style: .destructive, title: "Sil") {
            [weak self] _, _, completion in
            self?.contacts.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .automatic)
            completion(true)
        }
        return UISwipeActionsConfiguration(actions: [deleteAction])
    }
}

Delegate Performansı: boyut yöntemleri (heightForRowAt) her kaydırmada görünen her satır için çağrılır. Tek tip yükseklik için doğrudan rowHeight ayarlayın — bu heightForRowAt'tan 10 kat daha hızlıdır. Kendinden boyutlandıran hücreler için estimatedRowHeight ve automaticDimension ayarlayın — tablo heightForRowAt'ı yalnızca görünen satırlar için çağırır ve kalanlar için estimatedRowHeight kullanır.

Hücre yeniden kullanımı: yeniden kullanım kuyruğu ve kayıt

Yeniden kullanım kuyruğu — yeniden kullanılabilir UITableView hücrelerinden oluşan bir havuz. Bir hücre ekrandan kaydırıldığında silinmez, kuyruğa konur. Yeni hücreler sıfırdan oluşturulmaz — sistem dequeueReusableCell(withIdentifier:for:) çağırır, bu da kuyruktan bir hücre döndürür veya kuyruk boşsa yeni bir tane oluşturur. Bu, UITableView'in temel performans mekanizmasıdır.

swift
// Önbellekleme ile özel hücre
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 kısıtlamalarını yapılandırma
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        // ... constraints ...
    }

    func configure(with contact: Contact) {
        nameLabel.text = contact.name
        subtitleLabel.text = contact.phone
        // Async avatar yükleme
    }

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

// Kayıt ve kullanım
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")

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

prepareForReuse() — bir hücreyi yeniden kullanım kuyruğuna koymadan önce çağrılan bir yöntemdir. Tüm hücre durumunu sıfırlayın: metin, resimler, animasyonlar, yükleme işlemleri. prepareForReuse olmadan, yeniden kullanılan bir hücre eski verileri gösterebilir: önceki resim, yenisi yüklenene kadar saniyenin çok küçük bir kısmı kadar görünür olacaktır. Apple (WWDC 2023), prepareForReuse'da URLSessionTask.cancel() aracılığıyla asenkron işlemlerin iptal edilmesini önerir.

Bölümler, başlık ve altbilgi: veri gruplama

Bölümler UITableView'de mantıksal olarak ilişkili satırları gruplar. Her bölüm bir başlığa (header) ve altbilgiye (footer) sahip olabilir. Bölüm sayısı numberOfSections(in:) yöntemiyle belirlenir (varsayılan 1'dir). grouped ve insetGrouped stilleri için bölümler, kenar boşlukları ve yuvarlak köşelerle görsel olarak ayrılır.

DataSource YöntemiAçıklamaDönüş Değeri
numberOfSectionsBölüm sayısı (varsayılan 1)Int
numberOfRowsInSectionBir bölümdeki satır sayısıInt
titleForHeaderInSectionBölüm başlık metniString?
titleForFooterInSectionBölüm altbilgi metniString?
viewForHeaderInSectionBaşlık için özel görünümUIView?
heightForHeaderInSectionBaşlık yüksekliğiCGFloat

Dizinleme: bölümler arasında hızlı gezinme için, sağ tarafta görüntülenen bir dizi dizeden oluşan bir dizin başlığı ekleyin. sectionIndexTitles(for:) uygulayın — her bölümün ilk harflerinden oluşan bir dizi döndürür. 26+ bölüm için bu, kullanıcı deneyimini önemli ölçüde iyileştirir. iOS 15+'te tablolar sectionHeaderTopPadding'i destekler — durum çubuğu ile ilk başlık arasındaki dolgu, yoğun bir düzen için 0'a sıfırlanabilir.

UITableView Performansı: en iyi uygulamalar

Performans uzun listelere sahip uygulamalar için UITableView performansı kritiktir. Yaygın sorunlar: cellForRowAt'taki ağır işlemler nedeniyle yavaş kaydırma (takılma), sık heightForRowAt çağrıları, prefetching eksikliği. Apple (WWDC 2025) 1000+ öğeli tablolar için beş temel uygulamayı vurgulamaktadır.

  • Sabit yükseklik — tüm hücreler için heightForRowAt yerine sabit rowHeight kullanın. Fark: 500 satırlık bir tablo için 60 FPS'ye karşı 30 FPS.
  • Prefetching — hücre ekranda görünmeden önce asenkron veri yükleme (görüntüler, API) için UITableViewDataSourcePrefetching uygulayın.
  • Daha az alt görünüm — contentView'deki her alt görünüm işleme süresini artırır. UIImageView yerine basit grafikler (ayırıcılar, simgeler) için draw() kullanın.
  • Arka planda ağır işlemler — tarih biçimlendirme, yerelleştirme, hesaplamalar cellForRowAt'ta değil modelde yapılmalıdır. Sonuçlar için NSCache kullanın.
  • DiffableDataSource — reloadData() yerine apply(snapshot) kullanın — animasyon ücretsiz olarak gerçekleşir, tablo tamamen yeniden yüklenmez.
swift
// Prefetching ile optimizasyon
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)
        }
    }
}

// Pürüzsüz güncellemeler için DiffableDataSource kullanımı
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)
    }
}

Pratik sonuç: bu optimizasyonların uygulanması, iPhone 12 ve sonraki cihazlarda kaydırma FPS'sini 30'dan 60'a yükseltir (Apple test verilerine göre, 2025). IT Sectr projelerinde, tüm yeni tablolar için DiffableDataSource kullanıyoruz ve görseller (avatarlar, önizlemeler, uygulama simgeleri) içeren tablolara prefetching ekliyoruz.

Sıkça Sorulan Sorular

UITableView kaydırılmıyor — ne yapmalı?

Kontrol edin: (1) contentSize > frame.size — tablo, yüksekliğinden daha fazla içeriğe sahip olmalıdır; (2) tablo, jestleri engelleyen başka bir UIScrollView'in içinde değil; (3) isScrollEnabled = true; (4) numberOfRowsInSection doğru sayıyı döndürüyor. Tipik bir sorun, estimatedRowHeight olmadan heightForRowAt = UITableView.automaticDimension kullanan ve sonsuz yükseklik hesaplamasına neden olan bir tablodur.

UITableView titremeden nasıl güncellenir?

reloadData() yerine performBatchUpdates veya DiffableDataSource kullanın. reloadData() animasyon olmadan tam yeniden yüklemeye neden olur ve görsel titreme oluşturur. Hedeflenen güncellemeler için kullanın: reloadRows(at:with:), insertRows, deleteRows. performBatchUpdates içindeki tüm değişiklikler aynı anda canlandırılır. DiffableDataSource bunu manuel çağrılar olmadan otomatik olarak yapar.

UITableView ve UICollectionView arasındaki fark nedir?

UITableView, önceden tanımlanmış hücre stilleri ve yerleşik düzenleme ile tek sütunlu dikey bir listedir. UICollectionView, esnek bir düzendir (ızgara, liste, atlı karınca, şelale) ve öğelerin keyfi konumlandırılmasına olanak tanır. Basit listeler için (ayarlar, kişiler, mesajlar) UITableView seçin. Galeriler, kataloglar, panolar ve doğrusal olmayan düzenler için UICollectionView seçin.

UITableView'te içeriğin altındaki boş satırlar nasıl kaldırılır?

tableView.tableFooterView = UIView(frame: .zero) ayarlayın. Varsayılan olarak UITableView, son öğenin altından alt kenara kadar boş satırlar (ayırıcılar) görüntüler. Boş bir UIView'i tableFooterView olarak ayarlamak bunları kaldırır. grouped stili için bu gerekli değildir — tablo yalnızca son bölümde yuvarlak köşelerle gerçek satırları görüntüler.

Sabit yükseklikte UITableView nasıl yapılır?

tableView.rowHeight = 80 ve tableView.estimatedRowHeight = 0 ayarlayın veya estimatedRowHeight'ı atlayın. Sabit yükseklikle UITableView heightForRowAt'ı çağırmaz, bu da 100+ öğeli listelerde işlemeyi 5–10 kat hızlandırır. Özel yüksekliğe (farklı içerik) sahip hücreler için kendinden boyutlandırma kullanın: estimatedRowHeight + UITableView.automaticDimension.

Özet

  • UITableView — DataSource ve Delegate mimarisi ile dikey listeler için ana UIKit bileşeni.
  • UITableViewCell — 4 yerleşik stil (.default, .subtitle, .value1, .value2) ve contentConfiguration aracılığıyla özel.
  • Yeniden kullanım kuyruğu — yeniden kullanılabilir hücre havuzu; register + dequeueReusableCell + prepareForReuse zorunludur.
  • DiffableDataSource (iOS 13+) — otomatik değişiklik hesaplaması ve animasyon ile tip güvenli DataSource.
  • Performans: sabit rowHeight, prefetching, daha az alt görünüm, arka planda ağır işlemler.
  • App Store ilk 100'deki iOS uygulamalarının %85'i içerik görüntülemek için UITableView kullanır.
  • Izgaralar, atlı karıncalar ve karmaşık düzenler için UITableView yerine UICollectionView kullanın.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun