UITableView — iOS-da şaquli siyahıları göstərmək üçün UIKit-in əsas komponentidir. Cədvəlin hər sətri əvvəlcədən təyin edilmiş stilləri olan UITableViewCell hüceyrəsidir. UITableView-in əsaslarını izah edirik: DataSource, delegate, hüceyrələrin təkrar istifadəsi və iOS tətbiqlərində cədvəllərin performansı. Apple-ın məlumatına görə (Human Interface Guidelines, 2026), UITableView iOS-da ən çox istifadə edilən UI komponenti olaraq qalır — App Store top-100 tətbiqinin 85%-i ən azı bir cədvəl ehtiva edir. Mürəkkəb maketlər üçün UICollectionView haqqında məqaləni oxuyun.
Əsas məqamlar
UITableView — bir sütunda sürüşdürülə bilən sətir siyahısını göstərmək üçün UIKit freymvorkundan olan sinifdir. Hər sətir UITableViewCell obyekti ilə təmsil olunur. UITableView iPhone OS 2-də (2008) meydana çıxdı və o vaxtdan iOS-da siyahılar üçün əsas komponent olaraq qalır. UITableView-in arxitekturası MVC nümunəsi üzərində qurulub: məlumatlar DataSource tərəfindən, görünüş və davranış Delegate tərəfindən, görünüş isə cədvəlin özü tərəfindən idarə olunur.
İki cədvəl stili: .plain (isteğe bağlı bölmələri olan fasiləsiz siyahı) və .grouped (yuvarlaq küncləri və boşluqları olan qruplaşdırılmış bölmələr). iOS 13-dən etibarən .insetGrouped əlavə edildi — kənarlardan boşluqları olan grouped-stil, Parametrlər və Sağlamlıq tətbiqlərində istifadə olunur. Stil seçimi standart görünüşə təsir edir: plain-cədvəllər sürüşdürmə zamanı bölmə headerini yuxarıya yapışdırır (sticky header), grouped — yapışdırmır.
iOS 8-dən etibarən UITableView avtomatik ölçülü hüceyrələri (self-sizing cells) dəstəkləyir. Aktivləşdirmək üçün tableView.estimatedRowHeight (məsələn, 80) və tableView.rowHeight = UITableView.automaticDimension təyin edin. Cədvəl hüceyrə daxilindəki Auto Layout məhdudiyyətlərinə əsasən sətir hündürlüyünü avtomatik hesablayır. Self-sizing mürəkkəb hüceyrələr üçün performansı azaldır — 500+ elementi olan cədvəllər üçün sabit hündürlükdən (rowHeight) istifadə edin.
UITableViewCell xüsusi hüceyrə yaratmadan 80% ssenariləri əhatə edən dörd daxili stil təqdim edir. Hər stil textLabel, detailTextLabel və imageView kombinasiyasından ibarətdir. Stil seçimi hüceyrə işə salınarkən init(style:reuseIdentifier:) vasitəsilə təyin edilir.
| Stil | textLabel | detailTextLabel | imageView | Nümunə |
|---|---|---|---|---|
| .default (.basic) | Sol, qalın | Yox | İsteğe bağlı | Menu, element siyahısı |
| .subtitle | Sol, qalın | textLabel altında, boz | İsteğe bağlı | Kontaktlar, pleylistlər |
| .value1 | Sol, qalın | Sağ, boz | Yox | Dəyərləri olan parametrlər |
| .value2 | Sağ, mavi | Sol, boz | Yox | Telefon kitabçası (iOS 6 stili) |
// Xüsusi hüceyrələrlə cədvəl yaratma
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-ə birbaşa müraciət əvəzinə contentConfiguration vasitəsilə hüceyrələrin konfiqurasiyasının müasir üsulu. cell.defaultContentConfiguration() istifadə edin və ya xüsusi UIContentConfiguration yaradın. Üstünlükləri: dinamik tip, qaranlıq tema və VoiceOver dəstəyi qutudan çıxan kimi. Apple (WWDC 2024) bütün yeni UITableView-lər üçün contentConfiguration tövsiyə edir.
UITableViewDataSource — cədvəl üçün məlumat təmin edən protokol. Məcburi metodlar: tableView(_:numberOfRowsInSection:) və tableView(_:cellForRowAt:). Onlar olmadan cədvəl heç bir sətir göstərə bilməz. UITableViewDelegate — görünüş və davranışı idarə etmək üçün protokol: sətir hündürlüyü, seçim, redaktə, contextual actions.
// Bir neçə bölmə və redaktə ilə nümunə
extension ContactListController: UITableViewDelegate {
// Hər sətir üçün xüsusi hündürlük
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
80
}
// Seçimin işlənməsi
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ı: ölçü metodları (heightForRowAt) hər sürüşdürmədə hər görünən sətir üçün çağırılır. Bərabər hündürlük üçün birbaşa rowHeight təyin edin — bu heightForRowAt-dan 10 dəfə sürətlidir. Self-sizing hüceyrələri üçün estimatedRowHeight və automaticDimension təyin edin — cədvəl heightForRowAt-ı yalnız görünən sətirlər üçün çağıracaq, qalanları üçün estimatedRowHeight istifadə edəcək.
Reuse queue — təkrar istifadə olunan UITableView hüceyrələrinin hovuzu. Hüceyrə ekranı tərk etdikdə silinmir, növbəyə qoyulur. Yeni hüceyrələr sıfırdan yaradılmır — sistem dequeueReusableCell(withIdentifier:for:) çağırır, bu da növbədən hüceyrə qaytarır və ya növbə boşdursa yenisini yaradır. Bu, UITableView performansının əsas mexanizmidir.
// Keşləmə ilə xüsusi hüceyrə
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 məhdudiyyətlərinin qurulması
avatarView.translatesAutoresizingMaskIntoConstraints = false
// ... məhdudiyyətlər ...
}
func configure(with contact: Contact) {
nameLabel.text = contact.name
subtitleLabel.text = contact.phone
// Avatarın asinxron yüklənməsi
}
override func prepareForReuse() {
super.prepareForReuse()
avatarView.image = nil
nameLabel.text = nil
subtitleLabel.text = nil
}
}
// Qeydiyyat və istifadə
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
// cellForRowAt-də:
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactCell
cell.configure(with: contacts[indexPath.row])
return cell
prepareForReuse() — hüceyrə reuse queue-ya qoyulmazdan əvvəl çağırılan metod. Hüceyrənin bütün vəziyyətini sıfırlayın: mətn, şəkillər, animasiyalar, yükləmələr. prepareForReuse olmadan təkrar istifadə edilən hüceyrə köhnə məlumatları göstərə bilər: əvvəlki şəkil yenisi yüklənənə qədər saniyənin bir hissəsi görünəcək. Apple (WWDC 2023) prepareForReuse-də URLSessionTask.cancel() vasitəsilə asinxron əməliyyatların təmizlənməsini tövsiyə edir.
Bölmələr UITableView-də məntiqi əlaqəli sətirləri qruplaşdırır. Hər bölmənin header (başlıq) və footer (altlıq) ola bilər. Bölmələrin sayı numberOfSections(in:) metodu ilə müəyyən edilir (standart 1). Grouped və insetGrouped stilləri üçün bölmələr vizual olaraq boşluqlar və yuvarlaq künclərlə ayrılır.
| DataSource metodu | Təsvir | Qaytarılan dəyər |
|---|---|---|
| numberOfSections | Bölmələrin sayı (standart 1) | Int |
| numberOfRowsInSection | Bölmədəki sətirlərin sayı | Int |
| titleForHeaderInSection | Bölmə başlığının mətni | String? |
| titleForFooterInSection | Bölmə altlığının mətni | String? |
| viewForHeaderInSection | Header üçün xüsusi görünüş | UIView? |
| heightForHeaderInSection | Header hündürlüyü | CGFloat |
İndeksasiya: bölmələr üzrə sürətli naviqasiya üçün indeks əlavə edin — sağda göstərilən sətirlər massivi. sectionIndexTitles(for:) tətbiq edin — hər bölmənin ilk hərflərindən ibarət massiv qaytarır. 26+ bölmə üçün bu, UX-i ciddi şəkildə yaxşılaşdırır. iOS 15+-də cədvəllər sectionHeaderTopPadding dəstəkləyir — status barı ilə ilk header arasında sıx maket üçün 0-a endirilə bilən boşluq.
Performans UITableView uzun siyahıları olan tətbiqlər üçün kritik əhəmiyyət daşıyır. Əsas problemlər: cellForRowAt-da ağır əməliyyatlar səbəbindən yavaş sürüşdürmə (jank), heightForRowAt-ın tez-tez çağırılması, prefetching-in olmaması. Apple (WWDC 2025) 1000+ elementi olan cədvəllər üçün beş əsas təcrübəni vurğulayır.
// Prefetching ilə optimallaşdırma
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)
}
}
}
// Hamarlı yeniləmələr üçün DiffableDataSource istifadəsi
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)
}
}
Praktiki nəticə: bu optimallaşdırmaların tətbiqi iPhone 12 və daha köhnə cihazlarda sürüşdürmə FPS-ni 30-dan 60-a qaldırır (Apple testlərinə görə, 2025). IT Sectr layihələrində bütün yeni cədvəllər üçün DiffableDataSource istifadə edirik və şəkilləri olan cədvəllərə (avatar, önbaxış, tətbiq ikonları) prefetching əlavə edirik.
Tez-tez verilən suallar
Yoxlayın: (1) contentSize > frame.size — cədvəlin məzmunu hündürlüyündən çox olmalıdır; (2) cədvəl jestləri ələ keçirən başqa UIScrollView-in içərisində deyil; (3) isScrollEnabled = true; (4) numberOfRowsInSection düzgün say qaytarır. Tipik problem — estimatedRowHeight olmadan heightForRowAt = UITableView.automaticDimension, bu da sonsuz hündürlük hesablamasına səbəb olur.
performBatchUpdates və ya DiffableDataSource istifadə edin reloadData() əvəzinə. reloadData() animasiyasız tam yenidən yükləməyə səbəb olur, vizual yanıb-sönmə yaradır. Nöqtəvi yeniləmə üçün istifadə edin: reloadRows(at:with:), insertRows, deleteRows. performBatchUpdates daxilindəki bütün dəyişikliklər eyni anda animasiya olunur. DiffableDataSource bunu əl çağırışları olmadan avtomatik edir.
UITableView — əvvəlcədən təyin edilmiş hüceyrə stilləri və daxili redaktə ilə tək sütunlu şaquli siyahı. UICollectionView — elementlərin ixtiyari yerləşdirilməsi ilə çevik maket (grid, siyahı, karusel, kaskad). Sadə siyahılar üçün UITableView seçin (parametrlər, kontaktlar, mesajlar). Qalereyalar, kataloqlar, lövhələr və istənilən qeyri-xətti maketlər üçün UICollectionView seçin.
tableView.tableFooterView = UIView(frame: .zero) təyin edin. Standart olaraq UITableView son elementdən aşağı kənara qədər boş sətirləri (ayırıcıları) göstərir. tableFooterView-də boş UIView təyin etmək onları silir. Grouped-stil üçün bu tələb olunmur — cədvəl yalnız son bölmənin yuvarlaq küncləri ilə faktiki sətirləri göstərir.
tableView.rowHeight = 80 və tableView.estimatedRowHeight = 0 təyin edin və ya estimatedRowHeight-i buraxın. Sabit hündürlüklə UITableView heightForRowAt çağırmır, bu da 100+ elementi olan siyahılar üçün renderi 5–10 dəfə sürətləndirir. Xüsusi hündürlüklü hüceyrələr (müxtəlif məzmun) üçün self-sizing istifadə edin: estimatedRowHeight + UITableView.automaticDimension.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun