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, 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.
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, ö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.
| Stil | textLabel | detailTextLabel | imageView | Örnek |
|---|---|---|---|---|
| .default (.basic) | Sol, kalın | Yok | İsteğe bağlı | Menü, öğe listesi |
| .subtitle | Sol, kalın | textLabel altında, gri | İsteğe bağlı | Kişiler, çalma listeleri |
| .value1 | Sol, kalın | Sağ, gri | Yok | Değerli ayarlar |
| .value2 | Sağ, mavi | Sol, gri | Yok | Telefon rehberi (iOS 6 stili) |
// Ö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.
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.
// 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.
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.
// Ö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 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öntemi | Açıklama | Dönüş Değeri |
|---|---|---|
| numberOfSections | Bölüm sayısı (varsayılan 1) | Int |
| numberOfRowsInSection | Bir bölümdeki satır sayısı | Int |
| titleForHeaderInSection | Bölüm başlık metni | String? |
| titleForFooterInSection | Bölüm altbilgi metni | String? |
| viewForHeaderInSection | Başlık için özel görünüm | UIView? |
| heightForHeaderInSection | Başlık yüksekliği | CGFloat |
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.
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.
// 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
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.
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, ö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.
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.
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
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.
Ayrıca okuyun