17 Kas 2025·6 dk okuma

SwiftUI ile uzun listelerin performansını iyileştirme: pratik çözümler

Uzun SwiftUI listeleri için performans iyileştirmeleri: yeniden render'lar, kararlı satır kimliği, sayfalama, resim yükleme ve eski iPhone'larda akıcı kaydırma için pratik çözümler.

SwiftUI ile uzun listelerin performansını iyileştirme: pratik çözümler

SwiftUI uygulamalarında “yavaş listeler” nasıl görünür

SwiftUI'de bir “yavaş liste” genellikle bir hata değildir. Parmak hareketinize UI'nin yetişemediği andır. Kaydırırken fark edersiniz: liste tereddüt eder, kareler düşer ve her şey ağır hissedilir.

Tipik işaretler:

  • Özellikle eski cihazlarda kaydırma takılır
  • Satırlar titrer veya kısa süreliğine yanlış içerik gösterir
  • Dokunuşlar gecikir veya kaydırma işlemleri geç başlar
  • Telefon ısınır ve pil beklenenden hızlı tükenir
  • Kaydırdıkça bellek kullanımı büyür

Uzun listeler her satır “küçük” görünse bile yavaş hissedebilir, çünkü maliyet sadece pikselleri çizmek değildir. SwiftUI hâlâ her satırın ne olduğunu belirlemek, düzeni hesaplamak, yazı tiplerini ve resimleri çözmek, biçimlendirme kodunuzu çalıştırmak ve veri değiştiğinde güncellemeleri diff'lemek zorundadır. Bu işlerden herhangi biri çok sık oluyorsa, liste bir darboğaz haline gelir.

Ayrıca iki fikri ayırmak işe yarar. SwiftUI'de bir “yeniden render” genellikle bir görünümün body'sinin yeniden hesaplanması demektir. Bu kısım genelde ucuzdur. Pahalı olan, yeniden hesaplamanın tetiklediği iştir: ağır düzen, resim dekodlama, metin ölçümü veya SwiftUI'nin kimliğinin değiştiğini düşünmesi nedeniyle birçok satırın yeniden oluşturulması.

2.000 mesajlık bir sohbet hayal edin. Yeni mesajlar her saniye geliyor ve her satır zaman damgalarını biçimlendiriyor, çok satırlı metni ölçüyor ve avatarları yüklüyor. Sadece bir öğe ekleseniz bile kötü kapsamlanmış bir durum değişikliği birçok satırın yeniden değerlendirilmesine ve bazı satırların yeniden çizilmesine neden olabilir.

Amaç mikro-optimizasyon değil. Hedef akıcı kaydırma, anında dokunuş ve yalnızca gerçekten değişen satırları etkileyen güncellemeler. Aşağıdaki düzeltmeler kararlı kimlik, daha ucuz satırlar, gereksiz güncellemelerin azaltılması ve kontrollü yükleme üzerine odaklanır.

Temel nedenler: kimlik, satır başına iş ve güncelleme fırtınaları

Bir SwiftUI listesi yavaş hissettiğinde, nadiren sebep “çok fazla satır”dır. Kaydırma sırasında fazladan iş olur: satırların yeniden inşası, düzenin yeniden hesaplanması veya resimlerin defalarca yeniden yüklenmesi.

Çoğu temel neden üç gruba ayrılır:

  • Kararsız kimlik: satırların tutarlı bir id'si yok ya da değişebilen değerler için \.self kullanıyorsunuz. SwiftUI eski satırları yeni satırlarla eşleyemez, bu yüzden gereğinden fazla yeniden oluşturma olur.
  • Satır başına çok fazla iş: tarih biçimlendirme, filtreleme, resim yeniden boyutlandırma veya satır görünümünde ağ/diske dayalı işler yapmak.
  • Güncelleme fırtınaları: yazma, bir zamanlayıcı darbeleri veya ilerleme güncellemeleri gibi tek bir değişiklik sık sık durum güncellemeleri tetikler ve liste tekrar tekrar yenilenir.

Örnek: 2.000 siparişiniz var. Her satır para birimini biçimlendiriyor, atfedilmiş bir metin oluşturuyor ve bir resim fetch başlatıyor. Bu arada üst görünümdeki "son senkron" zamanı her saniye güncelleniyor. Sipariş verisi değişmese bile o zamanlayıcı listeyi yeterince sık geçersiz kılabilir ve kaydırma takılmasına neden olur.

List ve LazyVStack neden farklı hissedebilir

List bir scroll view'den daha fazlasıdır. Tablo/collection davranışı ve sistem optimizasyonları etrafında tasarlanmıştır. Genelde büyük veri kümeleriyle daha az bellek kullanarak iyi çalışır, ama kimlik ve sık güncellemeler konusunda hassas olabilir.

ScrollView + LazyVStack düzen ve görsellik üzerinde daha fazla kontrol verir, ama yanlışlıkla ekstra düzen işi yapmak veya pahalı güncellemeler tetiklemek daha kolaydır. Eski cihazlarda bu ekstra iş daha erken ortaya çıkar.

UI'inizi yeniden yazmadan önce ölçün. Kararlı ID'ler, satır dışına taşınmış işler ve durum çalkantısını azaltmak gibi küçük düzeltmeler genelde konteyner değiştirmeden sorunu çözer.

SwiftUI'nin verimli diff yapabilmesi için satır kimliğini düzeltin

Uzun bir liste takılma hissediyorsa, sebeplerin başında kimlik gelir. SwiftUI hangi satırların yeniden kullanılabileceğini ID karşılaştırarak belirler. Bu ID'ler değişirse, SwiftUI satırları yeni kabul eder, eskilerini atar ve gereğinden fazla yeniden oluşturma yapar. Bu, rastgele yeniden render'lar, kaydırma pozisyonunun kaybolması veya gereksiz animasyonlar gibi davranabilir.

En basit kazanım: her satırın id'sini veri kaynağınızla bağlantılı ve sabit yapın.

Yaygın bir hata, kimliği görünüm içinde üretmektir:

ForEach(items) { item in
  Row(item: item)
    .id(UUID())
}

Bu her render'da yeni bir ID üretir, böylece her satır her seferinde “farklı” olur.

Modelinizde zaten var olan ID'leri tercih edin; örneğin veritabanı birincil anahtarı, sunucu ID'si veya kararlı bir slug. Yoksa, görünüm içinde değil, model oluşturulduğunda bir tane oluşturun ve saklayın.

struct Item: Identifiable {
  let id: Int
  let title: String
}

List(items) { item in
  Row(item: item)
}

İndekslere dikkat edin. ForEach(items.indices, id: \\.self) kimliği pozisyona bağlar. Eğer ekleme, silme veya sıralama olursa, satırlar “taşınır” ve SwiftUI yanlış görünümü yeniden kullanabilir. Dizi hiç yeniden sıralanmıyorsa indeksleri kullanın.

id: \.self kullanıyorsanız, elemanın Hashable değeri zaman içinde sabit olsun. Hash, bir alan güncellendiğinde değişiyorsa, satırın kimliği de değişir. Equatable ve Hashable için güvenli kural: bunları tek, sabit bir ID'ye dayanacak şekilde oluşturun; düzenlenebilir özelliklere (ör. name, isSelected) dayandırmayın.

Kontrol listesi:

  • ID'ler veri kaynağından geliyor (görünüm içinde UUID() değil)
  • Satır içeriği değiştiğinde ID değişmiyor
  • Kimlik, liste asla yeniden sıralanmıyorsa dışındaki durumlarda dizi pozisyonuna bağlı değil

Yeniden render'ları azaltmak için satır görünümünü daha ucuz yapın

Uzun bir liste genelde her satır body'sinde çok fazla iş yapıldığı için yavaş hisseder. Hedef basit: her satır yeniden oluşturulduğunda ucuz olsun.

Gizli maliyetlerden biri, çok büyük değerleri satıra geçirmek olabilir. Büyük struct'lar, derin modeller veya ağır hesaplanan özellikler UI değişmese bile ekstra işe neden olabilir. Diziler, string'ler, tarih/numara biçimlendirme, resim yeniden boyutlandırma veya karmaşık layout ağaçları daha sık yeniden oluşturuluyor olabilir.

Ağır işleri body dışına taşıyın

Eğer bir şey yavaşsa, satır body'sinde defalarca yeniden oluşturmayın. Veri geldiğinde önceden hesaplayın, view model'de önbelleğe alın veya küçük bir yardımcı ile memoize edin.

Satır düzeyinde hız maliyeti getiren işler:

  • Her satırda yeni bir DateFormatter veya NumberFormatter oluşturmak
  • body içinde ağır string biçimlendirmeleri (join, regex, markdown parsing)
  • body içinde .map veya .filter ile türetilmiş diziler üretmek
  • Görünüm içinde büyük blob'ları okuyup dönüştürmek (ör. JSON decode)
  • Çok fazla iç içe stack ve koşul içeren aşırı karmaşık layout

Basit bir örnek: formatlayıcıları statik tutun ve satıra önceden biçimlendirilmiş string'ler gönderin.

enum Formatters {
    static let shortDate: DateFormatter = {
        let f = DateFormatter()
        f.dateStyle = .medium
        f.timeStyle = .none
        return f
    }()
}

struct OrderRow: View {
    let title: String
    let dateText: String

    var body: some View {
        HStack {
            Text(title)
            Spacer()
            Text(dateText).foregroundStyle(.secondary)
        }
    }
}

Satırları bölün ve Equatable kullanın (uygun olduğunda)

Eğer yalnızca küçük bir parça değişiyorsa (ör. bir rozet sayısı), onu alt görünüme izole edin ki satırın geri kalanı stabil kalsın. Değer odaklı UI için bir alt görünümü Equatable yapmak (veya EquatableView ile sarmak), girdiler değişmediğinde SwiftUI'nin işi atlamasına yardımcı olabilir. Equatable girdileri küçük ve spesifik tutun — tüm modeli değil.

Tam liste yenilenmelerini tetikleyen durum güncellemelerini kontrol edin

Nasıl gönderip barındıracağınıza karar verin
AppMaster Cloud, AWS, Azure, Google Cloud'a dağıtın veya kaynak kodu dışa aktararak kendi kendinize barındırın.
AppMaster'ı Keşfet

Bazen satırlar iyidir ama bir şey SwiftUI'ye tüm listeyi yenilemesini söylüyor. Kaydırma sırasında bile küçük ekstra güncellemeler takılmalara yol açabilir, özellikle eski cihazlarda.

Yaygın bir neden modeli çok sık yeniden oluşturmaktır. Bir üst görünüm yeniden oluşturuluyorsa ve siz görünümün sahip olduğu bir view modele @ObservedObject olarak bağlıysanız, SwiftUI onu yeniden oluşturabilir, abonlukları sıfırlayabilir ve yeni yayınlar tetikleyebilir. Görünüm modelin sahibi ise @StateObject kullanın, böylece bir kere oluşturulur ve stabil kalır. Dışarıdan enjekte edilen nesneler için @ObservedObject kullanın.

Sessiz bir performans katili de çok sık yayın yapmaktır. Zamanlayıcılar, Combine boru hatları ve ilerleme güncellemeleri saniyede birçok kez ateşleyebilir. Eğer yayımlanan bir özellik listeyi etkiliyorsa (veya ekranda kullanılan ortak bir ObservableObject üzerindeyse), her tik listeyi geçersiz kılabilir.

Örnek: Her tuş vuruşunda query'yi güncelleyen bir arama alanınız var ve 5.000 öğeyi filtreliyorsanız, hemen filtrelerseniz kullanıcı yazarken liste sürekli yeniden difflenir. Sorguyu debounce edin ve filtrelenmiş diziyi kısa bir duraklama sonrası güncelleyin.

Yardımcı desenler:

  • Hızla değişen değerleri listeyi yöneten nesnenin dışında tutun (daha küçük nesneler veya lokal @State kullanın)
  • Arama ve filtrelemeyi debounce edin, böylece liste yazma durakladıktan sonra güncellenir
  • Yüksek frekanslı zamanlayıcı yayınlarından kaçının; daha az sıklıkta güncelleyin veya yalnızca değer gerçekten değiştiğinde güncelleyin
  • Satır başına durumları lokal tutun (@State), global ve sık değişen bir değere bağlamayın
  • Büyük modelleri bölün: bir ObservableObject liste verileri için, diğeri ekran düzeyi UI durumu için olsun

Fikir basit: kaydırma zamanı sessiz olsun. Önemli bir şey değişmediyse, liste iş yapmak zorunda kalmamalı.

Doğru konteyneri seçin: List vs LazyVStack

Seçtiğiniz konteyner iOS'un sizin için ne kadar iş yaptığına etki eder.

List genelde klasik tablo görünümü için en güvenli seçimdir: metin, resimler, kaydırma işlemleri, seçim, ayırıcılar, düzenleme modu ve erişilebilirlik. Altında yıllardır Apple'ın geliştirdiği optimizasyonlardan faydalanır.

ScrollView + LazyVStack kartlar, karışık içerik blokları veya feed tarzı tasarım gibi özel düzenler için iyidir. “Lazy” olmasına rağmen, her durumda List ile aynı davranışı vermez. Çok büyük veri kümelerinde bu daha çok bellek kullanımı ve eski cihazlarda takılma anlamına gelebilir.

Basit bir karar kuralı:

  • Klasik tablo ekranları (ayarlar, gelen kutusu, siparişler) için List kullanın
  • Özel düzenler ve karışık içerik için ScrollView + LazyVStack kullanın
  • Binlerce öğeniz varsa ve sadece tabloya ihtiyacınız varsa, önce List ile başlayın
  • Piksel hassas kontrol istiyorsanız LazyVStack deneyin, sonra bellek ve kare düşüşlerini ölçün

Ayrıca kaydırmayı yavaşlatan stil unsurlarına dikkat edin. Satır başına gölge, blur ve karmaşık overlay'ler ekstra render işine zorlayabilir. Derinlik istiyorsanız ağır efektleri tüm satır yerine küçük bir öğede (ikon gibi) uygulayın.

Somut örnek: 5.000 satırlık bir “Siparişler” ekranı List içinde genelde akıcı kalır çünkü satırlar yeniden kullanılır. Eğer LazyVStack'e geçip kart tarzı satırlar, büyük gölgeler ve birçok overlay ile inşa ederseniz, kod temiz görünse bile takılma görebilirsiniz.

Hafıza sıçramalarını önleyen ve akıcı hissettiren sayfalama

Güncellemeleri kaynaktan azaltın
Görsel iş süreçleriyle iş akışlarını otomatikleştirip uygulama mantığını her yerde tutarlı kılın.
Proje Başlat

Sayfalama uzun listeleri hızlı tutar çünkü daha az satır render edilir, daha az model bellekte tutulur ve SwiftUI'ye daha az diff işi düşer.

Net bir sayfalama sözleşmesiyle başlayın: sabit bir sayfa boyutu (ör. 30–60 öğe), “daha fazla sonuç yok” bayrağı ve sadece fetch sırasında görünen bir yükleme satırı.

Yaygın tuzak, sonraki sayfayı yalnızca son satır göründüğünde tetiklemektir. Bu genelde çok geçtir ve kullanıcı sona geldiğinde bir duraklama görür. Bunun yerine son birkaç satırdan biri göründüğünde yüklemeye başlayın.

Basit bir örnek desen:

@State private var items: [Item] = []
@State private var isLoading = false
@State private var reachedEnd = false

func loadNextPageIfNeeded(currentIndex: Int) {
    guard !isLoading, !reachedEnd else { return }
    let threshold = max(items.count - 5, 0)
    guard currentIndex >= threshold else { return }

    isLoading = true
    Task {
        let page = try await api.fetchPage(after: items.last?.id)
        await MainActor.run {
            let newUnique = page.filter { p in !items.contains(where: { $0.id == p.id }) }
            items.append(contentsOf: newUnique)
            reachedEnd = page.isEmpty
            isLoading = false
        }
    }
}

Bu yaklaşım, yinelenen satırlar (API sonuçlarının örtüşmesi), birden fazla onAppear çağrısından kaynaklanan yarış durumları ve aşırı yüklemeyi önler.

Liste çekme yenilemesini destekliyorsanız, sayfalama durumunu dikkatle sıfırlayın (öğeleri temizleyin, reachedEnd'i sıfırlayın, mümkünse devam eden görevleri iptal edin). Backend'i kontrol edebiliyorsanız, kararlı ID'ler ve cursor-temelli sayfalama UI'yi belirgin şekilde daha akıcı yapar.

Resimler, metin ve düzen: satır render'ını hafif tutun

Satır işini UI'dan uzak tutun
Biçimlendirmeyi ve iş kurallarını backend'e taşıyarak satırların cihaz üzerinde hafif kalmasını sağlayın.
Uygulama İnşa Et

Uzun listelerin yavaş hissetmesinin nedeni genelde konteyner değil satırdır. Resimler en sık suçludur: dekodlama, yeniden boyutlandırma ve çizim kaydırma hızınızı aşabilir, özellikle eski cihazlarda.

Uzak resimler yüklüyorsanız, ağır işler kaydırma sırasında ana iş parçacığında gerçekleşmemeli. Ayrıca 44–80 pt mini görünüm için tam çözünürlük varlıkları indirmekten kaçının.

Örnek: avatarlar içeren bir “Mesajlar” ekranı düşünün. Her satır 2000x2000 bir görsel indirip küçültüyor ve blur veya gölge uyguluyorsa, liste takılacaktır.

Resim işini öngörülebilir tutun

Yüksek etki yaratan alışkanlıklar:

  • Sunucu tarafında veya önceden üretilmiş, gösterilen boyuta yakın küçük resimler kullanın
  • Dekodlama ve yeniden boyutlandırmayı ana iş parçacığından uzak yapmaya çalışın
  • Mini resimleri önbelleğe alın, böylece hızlı kaydırma yeniden çekme veya yeniden dekodlama yapmaz
  • Flicker ve düzen sıçramalarını önlemek için nihai boyuta uyan bir yer tutucu kullanın
  • Satırlardaki resimlerde ağır modifier'lardan (ağır gölgeler, maskeler, blur) kaçının

Düzeni stabil tutarak thrash'i önleyin

Satır yüksekliği sürekli değişiyorsa SwiftUI ölçüm yapmaya daha fazla zaman harcar. Satırları tahmin edilebilir tutmaya çalışın: küçük resimler için sabit frame'ler, tutarlı satır sınırları ve stabil boşluklar. Metin genişleyebiliyorsa; (ör. 1–2 satır) sınır koyun ki tek bir güncelleme ekstra ölçüm işine neden olmasın.

Yer tutucular da önemlidir. Daha sonra bir avatara dönüşecek gri bir daire aynı çerçeveyi işgal etmelidir, böylece satır kaydırma sırasında yeniden akmaz.

Ölçüm nasıl yapılır: gerçek darboğazları ortaya çıkaran Instruments kontrolleri

Sadece “takılıyor” diye hissetmekle performans işi kestirme olmaz. Instruments size ana iş parçacığında neyin çalıştığını, hızlı kaydırma sırasında neyin tahsis edildiğini ve hangi işlemlerin kare düşürmeye neden olduğunu gösterir.

Gerçek bir cihazda (destekliyorsanız daha eski bir cihazda) bir temel belirleyin. Tekrarlanabilir bir eylem yapın: ekranı açın, baştan sona hızlı kaydırın, bir kere daha yükle-yükle tetikleyin, sonra yukarı kaydırın. En kötü takılma noktalarını, bellek zirvesini ve UI'nin tepki verip vermediğini not edin.

En yararlı üç Instruments görünümü

Bunları birlikte kullanın:

  • Time Profiler: Kaydırırken ana iş parçacığındaki ani zirvelere bakın. Düzen hesaplaması, metin ölçümü, JSON parsing ve resim dekodlama burada genelde takılmayı açıklar.
  • Allocations: Hızlı kaydırma sırasında geçici nesnelerdeki sıçramaları izleyin. Bu genelde tekrarlanan biçimlendirme, yeni attributed string'ler veya satır başına model yeniden oluşturmayı işaret eder.
  • Core Animation: Düşen kareleri ve uzun frame sürelerini doğrulayın. Bu, darboğazın rendering mi yoksa veri işi mi olduğunu ayırt etmeye yardımcı olur.

Bir zirve bulduğunuzda, çağrı ağacına tıklayın ve sorun: bu ekran başına mı oluyor, yoksa kaydırmada her satır için mi? İkincisi akıcı kaydırmayı bozar.

Kaydırma ve sayfalama olayları için signpost ekleyin

Birçok uygulama kaydırma sırasında ekstra iş yapar (resim yükleri, sayfalama, filtreleme). Signpost'lar timeline üzerinde bu anları görmenize yardımcı olur.

import os
let log = OSLog(subsystem: "com.yourapp", category: "list")
os_signpost(.begin, log: log, name: "LoadMore")
// fetch next page
os_signpost(.end, log: log, name: "LoadMore")

Her değişiklikten sonra, adım adım tekrar test edin. FPS iyileşiyorsa ama Allocations kötüleşiyorsa, takılma için bellek baskısı ile takas yapmış olabilirsiniz. Temel notları saklayın ve yalnızca sayıları doğru yöne taşıyan değişiklikleri tutun.

Liste performansını sessizce öldüren yaygın hatalar

Gerçek veriyle uzun listeleri test edin
Sayfalama, önbellekleme ve temiz veri sözleşmeleriyle bir sohbet veya gelen kutusu prototipi oluşturun.
Şimdi Deneyin

Bazı sorunlar açıktır (büyük resimler, devasa veri setleri). Diğerleri veri büyüdükçe veya eski cihazlarda kendini gösterir.

1) Kararsız satır ID'leri

Görünüm içinde ID oluşturmak (ör. id: \.self referans tipleri için veya UUID()'yı satır gövdesinde kullanmak) klasik bir hatadır. SwiftUI güncellemeleri diff'lemek için kimliği kullanır. ID değişirse, satır yeni sayılır, yeniden oluşturulur ve önbelleğe alınmış düzen atılabilir.

Modelden gelen kararlı bir ID kullanın (veritabanı birincil anahtarı, sunucu ID'si veya model oluşturulduğunda saklanan UUID). Yoksa bir tane ekleyin.

2) onAppear içinde ağır işler

Satırlar kaydırma sırasında girip çıktığı için onAppear beklenenden daha sık çalışır. Eğer her satır burada resim dekodlama, JSON parsing veya veritabanı sorgusu başlatıyorsa tekrarlayan zirveler oluşur.

Ağır işleri satırdan çıkarın. Veri yüklendiğinde önceden hesaplayın, sonuçları önbelleğe alın ve onAppear'ı yalnızca sayfalamayı tetiklemek gibi ucuz görevlerle sınırlayın.

3) Tüm listeyi bağlayan bağlar ile satır düzenlemeleri

Her satıra büyük bir diziye @Binding geçirirseniz, küçük bir düzenleme büyük bir değişiklik gibi görünebilir. Bu, birçok satırın yeniden değerlendirilmesine ve bazen tüm listenin yenilenmesine yol açabilir.

Satıra immutable değerler gönderin ve değişiklikleri hafif bir eylemle geri iletin (ör. "id için favoriyi değiştir"). Satıra ait olan durumları yalnızca gerçekten oraya aitse satır içinde tutun.

4) Kaydırma sırasında çok fazla animasyon

Animasyonlar listede pahalıdır çünkü ekstra düzen geçişlerine neden olabilir. animation(.default, value:)'ı yüksek seviyede (bütün liste üzerinde) kullanmak veya sık değişen her durumu animasyonla yapmak kaydırmayı yapışkan hale getirebilir.

Basit tutun:

  • Animasyonları yalnızca değişen satırla sınırlayın
  • Hızlı kaydırma sırasında animasyondan kaçının (özellikle seçim/vurgulama için)
  • Sık değişen değerlerde implicit animasyonlardan kaçının
  • Karmaşık birleşik efektler yerine basit geçişleri tercih edin

Gerçek bir örnek: Her satır onAppear'da ağ isteği başlatan, UUID() ile id üreten ve "okundu" durumunu animasyonla değiştiren bir sohbet listesi. Bu kombinasyon sürekli satır değişimine neden olur. Kimliği düzeltmek, işi önbelleğe almak ve animasyonları sınırlamak aynı UI'yi anında daha akıcı hale getirebilir.

Hızlı kontrol listesi, basit bir örnek ve sonraki adımlar

Sadece birkaç şey yapacaksanız, şu adımlarla başlayın:

  • Her satır için kararlı, benzersiz bir id kullanın (dizi indeksi değil, görünüm içinde yeni oluşturulmuş UUID değil)
  • Satır işini küçük tutun: ağır biçimlendirmelerden, büyük view ağaçlarından ve body içinde pahalı hesaplamalardan kaçının
  • Yayınları kontrol edin: hızlı değişen durumların (zamanlayıcılar, yazma, ağ ilerlemesi) tüm listeyi geçersiz kılmasına izin vermeyin
  • Sayfalama ve ön getirme uygulayın ki bellek sabit kalsın
  • Ölçmeden önce ve sonra Instruments ile test edin, tahmin etmeyin

Bir destek gelen kutusunu düşünün: 20.000 konuşma. Her satır konu, son mesaj önizlemesi, zaman damgası, okunmamış rozeti ve bir avatar gösterir. Kullanıcılar arama yapabilir ve yeni mesajlar kaydırırken gelebilir. Yavaş versiyon genelde birkaç şeyi aynı anda yapar: her tuş vuruşunda satırları yeniden kurar, metni çok sık ölçer ve çok fazla resmi çok erken fetch eder.

Kod tabanınızı baştan yıkmadan uygulanabilecek pratik plan:

  • Temel: Kısa bir kaydırma ve arama oturumu kaydını Instruments'ta alın (Time Profiler + Core Animation).
  • Kimliği düzeltin: modelinizde gerçek bir id olduğundan ve ForEach'in bunu tutarlı kullandığından emin olun.
  • Sayfalama ekleyin: önce en yeni 50–100 öğeyi yükleyin, sonra kullanıcı sona yaklaştığında daha fazlasını alın.
  • Resimleri optimize edin: küçük thumbnail'lar kullanın, önbellekleme yapın ve ana iş parçacığında dekodlamadan kaçının.
  • Yeniden ölçüm: daha az düzen geçişi, daha az görünüm güncellemesi ve eski cihazlarda daha stabil kare süreleri doğrulayın.

Tam bir ürün (iOS uygulaması + backend + web admin paneli) inşa ediyorsanız, veri modeli ve sayfalama sözleşmesini erken tasarlamak da faydalıdır. AppMaster (appmaster.io) gibi platformlar bu tam-yığın iş akışı için tasarlanmıştır: veriyi ve iş mantığını görsel olarak tanımlamanıza izin verir ve yine de dağıtılabilir veya kendi kendinize barındırılabilir gerçek kaynak kodu oluşturur.

SSS

SwiftUI listem takılma yapıyorsa en hızlı düzeltme ne olmalı?

Önce satır kimliğini düzeltin. Modelinizden gelen kararlı bir id kullanın ve görünüm içinde ID üretmekten kaçının; çünkü ID değişirse SwiftUI satırları yeni olarak görür ve gereğinden fazla yeniden oluşturma yapar.

SwiftUI çok fazla “yeniden render” yaptığı için mi yavaş?

body yeniden hesaplaması genellikle ucuzdur; pahalı olan, bunun tetiklediği işlemlerdir. Ağır düzen hesaplaması, metin ölçümü, resim dekodlama ve kimliği değişken satırlar yüzünden yeniden oluşturulan çok sayıda satır genellikle kare düşmelerine yol açar.

ForEach ve List için kararlı bir `id` nasıl seçmeliyim?

Satır içinde UUID() kullanmayın veya verinin eklenip çıkarılabildiği durumlarda dizi indekslerine güvenmeyin. Tercihen sunucu/veritabanı ID'si veya model oluşturulduğunda saklanan bir UUID kullanın, böylece ID güncellemeler boyunca sabit kalır.

`id: \.self` liste performansını kötüleştirir mi?

id: \.self performansı kötüleştirebilir, özellikle de elemanın hash'i düzenlenebilir alanlar güncellendiğinde değişiyorsa; çünkü SwiftUI bunu farklı bir satır olarak görebilir. Eğer Hashable kullanmanız gerekiyorsa, kimliği tek bir kararlı değere dayandırın, name, isSelected veya türetilmiş metin gibi düzenlenebilir alanlara dayandırmayın.

Bir satırın `body`'sinde neler yapmamalıyım?

Ağır işleri body içinde yapmaktan kaçının. Tarih ve sayı biçimlendirmesini önceden yapın, her satır için yeni formatlayıcı oluşturmayın ve .map/.filter ile büyük türetilmiş diziler üretmek yerine bunları model veya view model içinde hesaplayıp hazır görüntü değerlerini satıra iletin.

Uzun bir listede neden `onAppear` bu kadar sık tetikleniyor?

onAppear kaydırma sırasında sıkça çalışır çünkü satırlar ekran girip çıktıkça tetiklenir. Her satır burada ağır işler (resim dekodlama, veritabanı okuma, parsing) başlatıyorsa tekrar eden zirveler oluşur; onAppear'ı sayfalama tetiklemek gibi ucuz işlemlerle sınırlayın.

Kaydırmayı yapışkan yapan “güncelleme fırtınalarına” ne sebep olur?

Listeyi etkileyen veya ekran tarafından paylaşılan hızlı değişen herhangi bir yayımlanan değer, satırı sık sık geçersiz kılabilir. Zamanlayıcılar, yazma durumları ve ilerleme güncellemelerini ana listeyi yöneten nesnenin dışında tutun, aramayı debounce edin ve gerektiğinde büyük ObservableObject'leri daha küçük parçalara ayırın.

Büyük veri setleri için `List` mi yoksa `LazyVStack` mı kullanmalıyım?

List klasik tablo benzeri ekranlar (metin, resim, kaydırma işlemleri, seçim, ayırıcılar) için genelde daha güvenlidir ve platform optimizasyonlarından yararlanır. ScrollView + LazyVStack ise özel düzenler için uygundur ama çok büyük veri setlerinde daha fazla bellek kullanımı ve takılma gösterebilir; hangisini kullanacağınızı tasarıma göre seçin ve ölçüm yapın.

Akıcı kalan basit bir sayfalama yaklaşımı nedir?

Çok geç yüklemeye başlamak yerine kullanıcının son birkaç satıra geldiğinde yeni sayfayı başlatın, isLoading ve reachedEnd gibi koruyucular kullanın ve sonuçları kararlı ID'lerle çoğaltmayın. Bu, son satıra ulaştığında görülen beklemeleri önler.

SwiftUI listemi yavaşlatanın ne olduğunu nasıl ölçerim?

Gerçek bir cihazda temel bir senaryo kaydedin ve Instruments ile ana iş parçacığı zirvelerini ve bellek tahsislerini inceleyin. Time Profiler, Allocations ve Core Animation kombinasyonu genellikle dar boğazın nerede olduğunu gösterir.

Başlaması kolay
Harika bir şey yaratın

Ücretsiz planla AppMaster ile denemeler yapın.
Hazır olduğunuzda uygun aboneliği seçebilirsiniz.

Başlayın