UITableView: grunder, listor och celler i iOS-appar

Författare: IT Sectr Publicerad: 2026-02-23 Lästid: 9 min

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 — UIKit-komponent för vertikala listor, tillgänglig sedan iOS 2.0 (iPhone OS 2).
  • UITableViewCell — tabellcell med fyra inbyggda stilar och anpassningsmöjligheter.
  • UITableViewDataSource — protokoll som bestämmer antalet rader och celler för varje sektion.
  • UITableViewDelegate — protokoll för hantering av markering, redigering och inställning av radhöjd.
  • UITableViewDiffableDataSource — modern DataSource (iOS 13+) med automatisk beräkning av ändringar.

Vad är UITableView i iOS?

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.

Self-Sizing Cells

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-stilar: basic, subtitle, value1, value2

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:).

StiltextLabeldetailTextLabelimageViewExempel
.default (.basic)Vänster, fetNejValfrittMeny, lista med objekt
.subtitleVänster, fetUnder textLabel, gråValfrittKontakter, spellistor
.value1Vänster, fetHöger, gråNejInställningar med värden
.value2Höger, blåVänster, gråNejTelefonbok (iOS 6-stil)
swift
// 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.

DataSource och Delegate: obligatoriska metoder

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.

swift
// 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.

Återanvändning av celler: reuse queue och register

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.

swift
// 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, header och footer: gruppering av data

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-metodBeskrivningReturvärde
numberOfSectionsAntal sektioner (standard 1)Int
numberOfRowsInSectionAntal rader i sektionInt
titleForHeaderInSectionText för sektionsrubrikString?
titleForFooterInSectionText för sektionssidfotString?
viewForHeaderInSectionAnpassad vy för headerUIView?
heightForHeaderInSectionHeader-höjdCGFloat

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.

UITableView-prestanda: bästa praxis

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.

  • Flat-height — använd konstant rowHeight istället för heightForRowAt för alla celler. Skillnad: 60 FPS vs 30 FPS för en tabell med 500 rader.
  • Prefetching — implementera UITableViewDataSourcePrefetching för asynkron laddning av data (bilder, API) innan cellen visas på skärmen.
  • Färre subview — varje subview i contentView ökar renderingstiden. Använd draw() för enkel grafik (avskiljare, ikoner) istället för UIImageView.
  • Tunga operationer i bakgrunden — utför datumformatering, lokalisering, beräkningar i modellen, inte i cellForRowAt. Använd NSCache för resultat.
  • DiffableDataSource — ersätt reloadData() med apply(snapshot) — animering sker gratis, tabellen laddas inte om helt.
swift
// 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

UITableView rullar inte — vad gör jag?

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.

Hur uppdaterar jag UITableView utan flimmer?

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.

Vad är skillnaden mellan UITableView och UICollectionView?

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.

Hur tar jag bort tomma rader under innehåll i UITableView?

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.

Hur skapar jag en UITableView med konstant höjd?

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

  • UITableView — den huvudsakliga UIKit-komponenten för vertikala listor med DataSource- och Delegate-arkitektur.
  • UITableViewCell — 4 inbyggda stilar (.default, .subtitle, .value1, .value2) och anpassade via contentConfiguration.
  • Reuse queue — pool av återanvändbara celler; register + dequeueReusableCell + prepareForReuse obligatoriskt.
  • DiffableDataSource (iOS 13+) — typsäker DataSource med automatisk ändringsberäkning och animering.
  • Prestanda: fast rowHeight, prefetching, färre subview, tunga operationer i bakgrunden.
  • 85% av iOS-apparna i App Store topp-100 använder UITableView för att visa innehåll.
  • För rutnät, karuseller och komplexa layouter, använd UICollectionView istället för UITableView.

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.

Diskutera projektet

Läs också