UITableView — den huvudsakliga UIKit-komponenten för att visa vertikala listor i iOS. Varje rad i tabellen är en UITableViewCell-cell med fördefinierade stilar. Vi förklarar grunderna i UITableView: DataSource, delegat, återanvändning av celler och prestanda för tabeller i iOS-appar. Enligt Apple (Human Interface Guidelines, 2026) är UITableView fortfarande den mest använda UI-komponenten i iOS — 85% av apparna i App Store topp-100 innehåller minst en tabell. För komplexa layouter läs artikeln om UICollectionView.
Huvudpunkter
UITableView — är en klass från UIKit-ramverket avsedd för att visa en rullningsbar lista med rader i en kolumn. Varje rad representeras av ett UITableViewCell-objekt. UITableView dök upp i iPhone OS 2 (2008) och har sedan dess varit huvudkomponenten för listor i iOS. Arkitekturen i UITableView är byggd på MVC-mönstret: data hanteras av DataSource, utseende och beteende av Delegate, och vyn av själva tabellen.
Två tabellstilar: .plain (kontinuerlig lista med valfria sektioner) och .grouped (grupperade sektioner med rundade hörn och marginaler). Sedan iOS 13 har .insetGrouped lagts till — grouped-stil med marginaler från kanterna, som används i apparna Inställningar och Hälsa. Stilvalet påverkar standardutseendet: plain-tabeller fäster sektionshuvudet överst vid rullning (sticky header), grouped — inte.
Sedan iOS 8 stöder UITableView celler med automatisk storlek (self-sizing cells). Aktivera genom att ställa in tableView.estimatedRowHeight (t.ex. 80) och tableView.rowHeight = UITableView.automaticDimension. Tabellen beräknar automatiskt radhöjden baserat på Auto Layout-begränsningar inuti cellen. Self-sizing minskar prestandan för komplexa celler — använd fast höjd (rowHeight) för tabeller med 500+ element.
UITableViewCell erbjuder fyra inbyggda stilar som täcker 80% av scenarierna utan att behöva skapa en anpassad cell. Varje stil består av en kombination av textLabel, detailTextLabel och imageView. Stilvalet ställs in vid initiering av cellen via init(style:reuseIdentifier:).
| Stil | textLabel | detailTextLabel | imageView | Exempel |
|---|---|---|---|---|
| .default (.basic) | Vänster, fet | Nej | Valfritt | Meny, lista med objekt |
| .subtitle | Vänster, fet | Under textLabel, grå | Valfritt | Kontakter, spellistor |
| .value1 | Vänster, fet | Höger, grå | Nej | Inställningar med värden |
| .value2 | Höger, blå | Vänster, grå | Nej | Telefonbok (iOS 6-stil) |
// Skapa tabell med anpassade celler
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+) — modernt sätt att konfigurera celler via contentConfiguration istället för direkt åtkomst till textLabel/detailTextLabel. Använd cell.defaultContentConfiguration() eller skapa en anpassad UIContentConfiguration. Fördelar: stöd för dynamisk typ, mörkt tema och VoiceOver direkt. Apple (WWDC 2024) rekommenderar contentConfiguration för alla nya UITableView.
UITableViewDataSource — protokoll som tillhandahåller data för tabellen. Obligatoriska metoder: tableView(_:numberOfRowsInSection:) och tableView(_:cellForRowAt:). Utan dem kan tabellen inte visa någon rad. UITableViewDelegate — protokoll för att hantera utseende och beteende: radhöjd, markering, redigering, contextual actions.
// Exempel med flera sektioner och redigering
extension ContactListController: UITableViewDelegate {
// Anpassad höjd för varje rad
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
80
}
// Hantering av markering
func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let contact = contacts[indexPath.row]
showDetail(for: contact)
}
// Svep för att ta bort
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
-> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: "Ta bort") {
[weak self] _, _, completion in
self?.contacts.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
completion(true)
}
return UISwipeActionsConfiguration(actions: [deleteAction])
}
}
Delegate-prestanda: storleksmetoder (heightForRowAt) anropas för varje synlig rad vid varje rullning. För enhetlig höjd, ställ in rowHeight direkt — detta är 10 gånger snabbare än heightForRowAt. För self-sizing-celler, ställ in estimatedRowHeight och automaticDimension — tabellen anropar heightForRowAt endast för synliga rader och använder estimatedRowHeight för resten.
Reuse queue — pool av återanvändbara UITableView-celler. När en cell lämnar skärmen tas den inte bort utan placeras i kön. Nya celler skapas inte från grunden — systemet anropar dequeueReusableCell(withIdentifier:for:), som returnerar en cell från kön eller skapar en ny om kön är tom. Detta är den viktigaste prestationsmekanismen i UITableView.
// Anpassad cell med cachning
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)
// Konfigurera Auto Layout-begränsningar
avatarView.translatesAutoresizingMaskIntoConstraints = false
// ... begränsningar ...
}
func configure(with contact: Contact) {
nameLabel.text = contact.name
subtitleLabel.text = contact.phone
// Asynkron laddning av avatar
}
override func prepareForReuse() {
super.prepareForReuse()
avatarView.image = nil
nameLabel.text = nil
subtitleLabel.text = nil
}
}
// Registrering och användning
tableView.register(ContactCell.self, forCellReuseIdentifier: "contactCell")
// I cellForRowAt:
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactCell
cell.configure(with: contacts[indexPath.row])
return cell
prepareForReuse() — metod som anropas innan cellen placeras i reuse queue. Återställ hela cellens tillstånd: text, bilder, animationer, laddningar. Utan prepareForReuse kan en återanvänd cell visa gamla data: den föregående bilden syns en bråkdels sekund tills den nya laddas. Apple (WWDC 2023) rekommenderar rensning av asynkrona operationer via URLSessionTask.cancel() i prepareForReuse.
Sektioner i UITableView grupperar logiskt relaterade rader. Varje sektion kan ha header (rubrik) och footer (sidfot). Antalet sektioner bestäms av metoden numberOfSections(in:) (standard 1). För grouped och insetGrouped stilar är sektionerna visuellt åtskilda med marginaler och rundade hörn.
| DataSource-metod | Beskrivning | Returvärde |
|---|---|---|
| numberOfSections | Antal sektioner (standard 1) | Int |
| numberOfRowsInSection | Antal rader i sektion | Int |
| titleForHeaderInSection | Text för sektionsrubrik | String? |
| titleForFooterInSection | Text för sektionssidfot | String? |
| viewForHeaderInSection | Anpassad vy för header | UIView? |
| heightForHeaderInSection | Header-höjd | CGFloat |
Indexering: för snabb navigering mellan sektioner, lägg till ett index — en strängarray som visas till höger. Implementera sectionIndexTitles(for:) — returnerar en array med första bokstaven i varje sektion. För 26+ sektioner förbättrar detta UX avsevärt. I iOS 15+ stöder tabeller sectionHeaderTopPadding — utrymmet mellan statusfältet och första headern som kan återställas till 0 för kompakt layout.
Prestanda UITableView är avgörande för appar med långa listor. Huvudproblem: långsam rullning (jank) på grund av tunga operationer i cellForRowAt, frekventa anrop till heightForRowAt, brist på prefetching. Apple (WWDC 2025) lyfter fram fem viktiga metoder för tabeller med 1000+ element.
// Optimering med 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)
}
}
}
// Använda DiffableDataSource för smidiga uppdateringar
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)
}
}
Praktiskt resultat: tillämpning av dessa optimeringar ökar rullnings-FPS från 30 till 60 på iPhone 12 och äldre enheter (enligt Apple-tester, 2025). I IT Sectr-projekt använder vi DiffableDataSource för alla nya tabeller och lägger till prefetching för tabeller med bilder (avatarer, förhandsvisningar, appikoner).
Vanliga frågor
Kontrollera: (1) contentSize > frame.size — tabellen måste ha mer innehåll än sin höjd; (2) tabellen är inte inuti en annan UIScrollView som fångar gester; (3) isScrollEnabled = true; (4) numberOfRowsInSection returnerar rätt antal. Typiskt problem — en tabell med heightForRowAt = UITableView.automaticDimension utan estimatedRowHeight, vilket leder till oändlig höjdberäkning.
Använd performBatchUpdates eller DiffableDataSource istället för reloadData(). reloadData() orsakar full omladdning utan animering, vilket skapar visuellt flimmer. För punktuppdateringar använd: reloadRows(at:with:), insertRows, deleteRows. Alla ändringar inom performBatchUpdates animeras samtidigt. DiffableDataSource gör detta automatiskt utan manuella anrop.
UITableView — enkolumnig vertikal lista med fördefinierade cellstilar och inbyggd redigering. UICollectionView — flexibel layout (rutnät, lista, karusell, kaskad) med godtycklig placering av element. Välj UITableView för enkla listor (inställningar, kontakter, meddelanden). Välj UICollectionView för gallerier, kataloger, anslagstavlor och alla icke-linjära layouter.
Ställ in tableView.tableFooterView = UIView(frame: .zero). Som standard visar UITableView tomma rader (avskiljare) under det sista elementet ner till bottenkanten. Att ställa in en tom UIView i tableFooterView tar bort dem. För grouped-stil krävs detta inte — tabellen visar endast de faktiska raderna med rundade hörn i den sista sektionen.
Ställ in tableView.rowHeight = 80 och tableView.estimatedRowHeight = 0 eller utelämna estimatedRowHeight. Med fast höjd anropar UITableView inte heightForRowAt, vilket snabbar upp renderingen 5–10 gånger för listor med 100+ element. För celler med anpassad höjd (olika innehåll) använd self-sizing: estimatedRowHeight + UITableView.automaticDimension.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också