Hiệu Suất SwiftUI: Tối Ưu Hóa LazyVStack và Danh Sách Phức Tạp
Kỹ thuật tối ưu hóa cho LazyVStack và danh sách SwiftUI. Giảm tiêu thụ bộ nhớ, cải thiện hiệu suất cuộn và tránh các lỗi thường gặp.

Danh sach dai dien cho mot trong nhung thanh phan duoc su dung nhieu nhat trong cac ung dung iOS. LazyVStack va List cua SwiftUI cung cap cac giai phap hieu suat cao de hien thi cac tap hop du lieu, nhung viec su dung khong dung cach co the nhanh chong lam suy giam trai nghiem nguoi dung. Hieu duoc co che ben trong cua cac thanh phan nay se giup tranh duoc cac cam bay pho bien va xay dung giao dien muot ma.
Bai viet nay trinh bay cac ky thuat toi uu hoa thiet yeu cho danh sach SwiftUI: lazy loading, tai su dung view, quan ly dinh danh va cac mau nang cao cho tap du lieu lon. Da cap nhat cho iOS 27 voi cac API reordering va swipe action moi.
Hieu Ve Lazy Loading Trong SwiftUI
Lazy loading khoi tao view chi khi chung tro nen hien thi tren man hinh. Khac voi VStack tao tat ca cac view con ngay lap tuc, LazyVStack tri hoan viec tao nay, giam dang ke tieu thu bo nho va thoi gian render ban dau.
import SwiftUI
// ❌ Problem: VStack instantiates all 10,000 views immediately
struct NonLazyListView: View {
let items = (1...10000).map { "Item (/bin/bash)" }
var body: some View {
ScrollView {
VStack {
ForEach(items, id: .self) { item in
// Each view is created at launch
ExpensiveRowView(title: item)
}
}
}
}
}
// ✅ Solution: LazyVStack creates views on demand
struct LazyListView: View {
let items = (1...10000).map { "Item (/bin/bash)" }
var body: some View {
ScrollView {
LazyVStack {
ForEach(items, id: .self) { item in
// Only visible views are created
ExpensiveRowView(title: item)
}
}
}
}
}Su khac biet ve hieu suat tro nen dang ke chi voi vai tram phan tu. Voi 10.000 muc, VStack co the mat vai giay de khoi dong trong khi LazyVStack van tuc thi.
Do Luong Tac Dong Cua Lazy Loading
Instruments cho phep do luong chinh xac viec su dung bo nho va CPU. Day la mot view kiem tra minh hoa su khac biet:
struct ExpensiveRowView: View {
let title: String
// Simulating expensive initialization
init(title: String) {
self.title = title
// Log to visualize when the view is created
print("Creating row: (title)")
}
var body: some View {
HStack {
// Image with processing
Circle()
.fill(
LinearGradient(
colors: [.blue, .purple],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 50, height: 50)
VStack(alignment: .leading) {
Text(title)
.font(.headline)
Text("Subtitle with computation")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
.padding()
}
}Khi chay voi VStack, tat ca 10.000 log xuat hien ngay lap tuc. Voi LazyVStack, chi cac phan tu hien thi (khoang 15-20 tuy thuoc vao kich thuoc man hinh) duoc ghi log ban dau, voi nhieu hon xuat hien khi cuon.
LazyVStack giu cac view da tao trong bo nho sau khi chung xuat hien. Khac voi List chu dong tai su dung cac o, cac view trong LazyVStack ton tai cho den khi thanh phan cha bi huy.
Tam Quan Trong Cua Dinh Danh On Dinh
Dinh danh tao thanh co che trung tam cho viec cap nhat danh sach SwiftUI. Mot dinh danh khong on dinh gay ra viec tao lai view khong can thiet va co the kich hoat cac loi hien thi nhu animation sai hoac mat vi tri cuon.
// ❌ Problem: using index as identifier
struct UnstableIdentifierView: View {
@State private var items = ["A", "B", "C", "D"]
var body: some View {
List {
// Index changes if an element is deleted
ForEach(items.indices, id: .self) { index in
Text(items[index])
}
}
}
}
// ❌ Problem: using UUID() in ForEach
struct RegeneratedIdentifierView: View {
let items = ["A", "B", "C", "D"]
var body: some View {
List {
// UUID() generates a new ID on each render
ForEach(items, id: .self) { item in
// Subtle issue if items contain duplicates
Text(item)
}
}
}
}
// ✅ Solution: model with stable identifier
struct Item: Identifiable {
let id: UUID // Created once
var name: String
init(name: String) {
self.id = UUID()
self.name = name
}
}
struct StableIdentifierView: View {
@State private var items = [
Item(name: "A"),
Item(name: "B"),
Item(name: "C"),
Item(name: "D")
]
var body: some View {
List {
// id is stable for the item's lifetime
ForEach(items) { item in
Text(item.name)
}
}
}
}Viec su dung dinh danh duy nhat va lien tuc dam bao SwiftUI co the phan biet chinh xac cac phan tu trong cac cap nhat, animation va so sanh.
Toi Uu O Voi Equatable
SwiftUI so sanh cac view de xac dinh lieu co can render lai hay khong. Theo mac dinh, so sanh nay su dung reflection, co the ton kem. Viec trien khai Equatable cho phep so sanh duoc toi uu hoa va ro rang.
// Data model
struct Contact: Identifiable, Equatable {
let id: UUID
var name: String
var email: String
var avatarURL: URL?
var lastActivity: Date
// Custom comparison: ignore lastActivity
// if other properties are identical
static func == (lhs: Contact, rhs: Contact) -> Bool {
lhs.id == rhs.id &&
lhs.name == rhs.name &&
lhs.email == rhs.email &&
lhs.avatarURL == rhs.avatarURL
// lastActivity intentionally excluded
}
}
// Optimized cell view
struct ContactRow: View, Equatable {
let contact: Contact
// Explicit comparison to avoid unnecessary re-renders
static func == (lhs: ContactRow, rhs: ContactRow) -> Bool {
lhs.contact == rhs.contact
}
var body: some View {
HStack(spacing: 12) {
// Async avatar
AsyncImage(url: contact.avatarURL) { phase in
switch phase {
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill)
case .failure:
Image(systemName: "person.circle.fill")
.foregroundStyle(.gray)
default:
ProgressView()
}
}
.frame(width: 44, height: 44)
.clipShape(Circle())
// Contact information
VStack(alignment: .leading, spacing: 2) {
Text(contact.name)
.font(.body.weight(.medium))
Text(contact.email)
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer()
}
.padding(.vertical, 4)
}
}
// List using EquatableView
struct ContactListView: View {
let contacts: [Contact]
var body: some View {
List {
ForEach(contacts) { contact in
// EquatableView prevents re-renders if contact unchanged
EquatableView(content: ContactRow(contact: contact))
}
}
}
}Toi uu hoa nay giam dang ke tai CPU trong qua trinh cuon nhanh, dac biet voi cac o chua tinh toan hoac hinh anh.
Sẵn sàng chinh phục phỏng vấn iOS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Quan Ly Tai Hinh Anh Bat Dong Bo
Hinh anh thuong tro thanh diem nghen hieu suat trong danh sach. iOS 27 da gioi thieu caching HTTP tieu chuan cho AsyncImage theo mac dinh, loai bo nhu cau caching thu cong trong nhieu tinh huong. Doi voi cac ung dung nham vao iOS 26 va truoc do, hoac yeu cau kiem soat caching chi tiet hon, viec trien khai tuy chinh van co gia tri.
import SwiftUI
// Singleton image cache (for iOS 26 or custom caching needs)
actor ImageCache {
static let shared = ImageCache()
private var cache = NSCache<NSString, UIImage>()
private init() {
// Memory limit: 50 MB
cache.totalCostLimit = 50 * 1024 * 1024
}
func image(for url: URL) -> UIImage? {
cache.object(forKey: url.absoluteString as NSString)
}
func setImage(_ image: UIImage, for url: URL) {
// Cost estimation: image bytes
let cost = Int(image.size.width * image.size.height * 4)
cache.setObject(image, forKey: url.absoluteString as NSString, cost: cost)
}
}
// Optimized image view with caching
struct CachedAsyncImage: View {
let url: URL?
let size: CGSize
@State private var image: UIImage?
@State private var isLoading = false
var body: some View {
Group {
if let image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
} else if isLoading {
Rectangle()
.fill(Color.gray.opacity(0.2))
.overlay(ProgressView())
} else {
Rectangle()
.fill(Color.gray.opacity(0.2))
}
}
.frame(width: size.width, height: size.height)
.clipped()
.task(id: url) {
await loadImage()
}
}
private func loadImage() async {
guard let url else { return }
// Check cache
if let cached = await ImageCache.shared.image(for: url) {
self.image = cached
return
}
isLoading = true
defer { isLoading = false }
// Download and resize
do {
let (data, _) = try await URLSession.shared.data(from: url)
// Resize to save memory
if let original = UIImage(data: data),
let resized = await resizeImage(original, to: size) {
await ImageCache.shared.setImage(resized, for: url)
self.image = resized
}
} catch {
// Handle error silently
}
}
private func resizeImage(_ image: UIImage, to size: CGSize) async -> UIImage? {
// Use screen scale
let scale = await UIScreen.main.scale
let targetSize = CGSize(
width: size.width * scale,
height: size.height * scale
)
return await withCheckedContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
let renderer = UIGraphicsImageRenderer(size: targetSize)
let resized = renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: targetSize))
}
continuation.resume(returning: resized)
}
}
}
}Doi voi iOS 27 va moi hon, AsyncImage tieu chuan ton trong cac header cache HTTP tu dong. URLCache tuy chinh co the duoc ap dung thong qua modifier asyncImageURLSession(_:) cho cac yeu cau cu the.
Prefetching Thong Minh De Du Doan
Doi voi danh sach rat dai, prefetching tai hinh anh truoc khi chung tro nen hien thi:
// Prefetching coordinator
@Observable
final class ImagePrefetcher {
private var prefetchTasks: [URL: Task<Void, Never>] = [:]
private let prefetchDistance = 10 // Number of items ahead
func prefetchImages(for items: [Contact], visibleRange: Range<Int>) {
// Calculate prefetch range
let prefetchStart = max(0, visibleRange.lowerBound - prefetchDistance)
let prefetchEnd = min(items.count, visibleRange.upperBound + prefetchDistance)
// Launch prefetch for items in range
for index in prefetchStart..<prefetchEnd {
guard let url = items[index].avatarURL else { continue }
// Avoid duplicates
guard prefetchTasks[url] == nil else { continue }
prefetchTasks[url] = Task {
// Check if already cached
if await ImageCache.shared.image(for: url) != nil {
return
}
// Prefetch
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let image = UIImage(data: data) {
await ImageCache.shared.setImage(image, for: url)
}
} catch {
// Ignore prefetch errors
}
}
}
// Cancel out-of-range prefetches
cancelOutOfRangePrefetches(validRange: prefetchStart..<prefetchEnd, items: items)
}
private func cancelOutOfRangePrefetches(validRange: Range<Int>, items: [Contact]) {
let validURLs = Set(
items[validRange].compactMap { /bin/bash.avatarURL }
)
for (url, task) in prefetchTasks {
if !validURLs.contains(url) {
task.cancel()
prefetchTasks.removeValue(forKey: url)
}
}
}
}Lua Chon Giua List va LazyVStack
iOS 27 da thu hep dang ke khoang cach giua List va LazyVStack. Truoc day, List la lua chon duy nhat cho swipe action native va drag-to-reorder. Cac API .swipeActionsContainer() va .reorderable() moi mang nhung kha nang nay den bat ky container nao.
// ✅ List: automatic cell recycling, built-in styles
// Best for: standard contacts, settings, data tables
struct ContactsWithSwipeActions: View {
@State private var contacts: [Contact] = []
var body: some View {
List {
ForEach(contacts) { contact in
ContactRow(contact: contact)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
deleteContact(contact)
} label: {
Label("Delete", systemImage: "trash")
}
}
.swipeActions(edge: .leading) {
Button {
favoriteContact(contact)
} label: {
Label("Favorite", systemImage: "star")
}
.tint(.yellow)
}
}
}
.listStyle(.plain)
}
private func deleteContact(_ contact: Contact) {
contacts.removeAll { /bin/bash.id == contact.id }
}
private func favoriteContact(_ contact: Contact) {
// Favorite logic
}
}
// ✅ LazyVStack with iOS 27 swipe actions
// Best for: custom layouts, cards, feeds
struct CustomFeedWithSwipeActions: View {
@State private var posts: [Post] = []
var body: some View {
ScrollView {
LazyVStack(spacing: 16) {
ForEach(posts) { post in
PostCard(post: post)
.swipeActions(edge: .trailing) {
Button(role: .destructive) {
deletePost(post)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}
.padding(.horizontal)
.swipeActionsContainer()
}
}
private func deletePost(_ post: Post) {
posts.removeAll { /bin/bash.id == post.id }
}
}Lua chon bay gio phu thuoc vao yeu cau truc quan thay vi tinh kha dung cua tinh nang:
| Yeu cau | Container Duoc Khuyen Nghi |
|---|---|
| Tai su dung o de tiet kiem bo nho | List |
| Spacing va padding tuy chinh | LazyVStack |
| Separator va inset tich hop | List |
| Layout dang the hoac khong tieu chuan | LazyVStack |
| Header section voi hanh vi sticky | Ca hai (su dung pinnedViews) |
| Swipe actions | Ca hai (iOS 27+) |
| Drag-to-reorder | Ca hai (iOS 27+) |
List chu dong tai su dung cac o, co the gay ra van de voi state cuc bo (@State). Cac gia tri @State trong cac o List co the duoc su dung lai bat ngo. Nen luu tru state trong mo hinh du lieu hoac trong ViewModel.
Reordering Item trong LazyVStack (iOS 27)
Truoc iOS 27, drag-to-reorder yeu cau List voi onMove(perform:). Cac API reorderable moi hoat dong voi bat ky container nao, bao gom LazyVStack va LazyVGrid.
import SwiftUI
struct ReorderableCardList: View {
@State private var cards: [Card] = Card.sampleData
var body: some View {
ScrollView {
LazyVStack(spacing: 12) {
ForEach(cards) { card in
CardView(card: card)
}
.reorderable() // Mark content as draggable
}
.padding()
.reorderContainer(for: Card.self) { difference in
// Apply the reorder to the model
cards.apply(difference: difference)
}
}
}
}
struct Card: Identifiable {
let id: UUID
var title: String
var color: Color
static var sampleData: [Card] {
[
Card(id: UUID(), title: "Design Review", color: .blue),
Card(id: UUID(), title: "Code Sprint", color: .green),
Card(id: UUID(), title: "Testing Phase", color: .orange)
]
}
}
struct CardView: View {
let card: Card
var body: some View {
Text(card.title)
.font(.headline)
.frame(maxWidth: .infinity)
.padding()
.background(card.color.opacity(0.2))
.cornerRadius(12)
}
}
// Extension to apply ReorderDifference
extension Array where Element: Identifiable {
mutating func apply(difference: ReorderDifference<Element.ID>) {
// Move items from sources to destination
let movedItems = difference.sources.compactMap { id in
self.first { /bin/bash.id == id }
}
// Remove from original positions
self.removeAll { item in
difference.sources.contains(item.id)
}
// Insert at destination
if let destinationIndex = self.firstIndex(where: { /bin/bash.id == difference.destination }) {
self.insert(contentsOf: movedItems, at: destinationIndex)
} else {
self.append(contentsOf: movedItems)
}
}
}SwiftUI xu ly drag preview, placeholder chen va animation drop. Doi tuong ReorderDifference cung cap cac ID nguon va dich, de lai viec cap nhat mo hinh cho ma ung dung.
Section va Header Duoc Toi Uu
To chuc noi dung thanh cac section cai thien kha nang doc nhung co the anh huong den hieu suat neu duoc trien khai kem. Header duoc pin va quan ly section doi hoi su chu y dac biet.
// Grouped data model
struct GroupedContacts {
let letter: String
let contacts: [Contact]
}
// View with optimized sections
struct SectionedContactList: View {
let groupedContacts: [GroupedContacts]
var body: some View {
ScrollView {
LazyVStack(spacing: 0, pinnedViews: [.sectionHeaders]) {
ForEach(groupedContacts, id: .letter) { group in
Section {
// Section content
ForEach(group.contacts) { contact in
ContactRow(contact: contact)
.padding(.horizontal)
.padding(.vertical, 8)
// Custom separator
if contact.id != group.contacts.last?.id {
Divider()
.padding(.leading, 68)
}
}
} header: {
// Optimized pinned header
SectionHeader(title: group.letter)
}
}
}
}
}
}
// Lightweight header for performance
struct SectionHeader: View {
let title: String
var body: some View {
Text(title)
.font(.headline)
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.vertical, 8)
.background(.ultraThinMaterial)
}
}
// Optimized grouping function
extension Array where Element == Contact {
func groupedByFirstLetter() -> [GroupedContacts] {
// Dictionary for O(n) grouping
var groups: [String: [Contact]] = [:]
for contact in self {
let letter = String(contact.name.prefix(1)).uppercased()
groups[letter, default: []].append(contact)
}
// Sort groups alphabetically
return groups
.map { GroupedContacts(letter: /bin/bash.key, contacts: /bin/bash.value) }
.sorted { /bin/bash.letter < .letter }
}
}Header duoc pin (pinnedViews: [.sectionHeaders]) van hien thi trong khi cuon, cai thien dieu huong trong cac danh sach dai.
Phan Trang va Cuon Vo Han
Doi voi tap du lieu lon, phan trang tranh tai tat ca du lieu vao bo nho. Viec trien khai phai minh bach doi voi nguoi dung.
// ViewModel handling pagination
@Observable
final class PaginatedListViewModel {
private(set) var items: [Contact] = []
private(set) var isLoading = false
private(set) var hasMorePages = true
private var currentPage = 0
private let pageSize = 20
private let dataService: ContactDataService
init(dataService: ContactDataService) {
self.dataService = dataService
}
func loadInitialData() async {
guard items.isEmpty else { return }
await loadNextPage()
}
func loadMoreIfNeeded(currentItem: Contact) async {
// Trigger loading when approaching the end
guard let index = items.firstIndex(where: { /bin/bash.id == currentItem.id }) else {
return
}
// Load 5 items before the end
let thresholdIndex = items.count - 5
if index >= thresholdIndex {
await loadNextPage()
}
}
private func loadNextPage() async {
guard !isLoading, hasMorePages else { return }
isLoading = true
defer { isLoading = false }
do {
let newItems = try await dataService.fetchContacts(
page: currentPage,
limit: pageSize
)
items.append(contentsOf: newItems)
currentPage += 1
hasMorePages = newItems.count == pageSize
} catch {
// Handle error
}
}
}
// View with infinite scroll
struct InfiniteContactList: View {
@State private var viewModel: PaginatedListViewModel
init(dataService: ContactDataService) {
_viewModel = State(initialValue: PaginatedListViewModel(dataService: dataService))
}
var body: some View {
List {
ForEach(viewModel.items) { contact in
ContactRow(contact: contact)
.task {
// Check if more loading needed
await viewModel.loadMoreIfNeeded(currentItem: contact)
}
}
// Loading indicator at end of list
if viewModel.isLoading {
HStack {
Spacer()
ProgressView()
Spacer()
}
.padding()
}
}
.task {
await viewModel.loadInitialData()
}
}
}
// Protocol for data service
protocol ContactDataService {
func fetchContacts(page: Int, limit: Int) async throws -> [Contact]
}Mau nay dam bao tai muot ma ma khong chan giao dien va cho phep quan ly bo nho hieu qua.
Sẵn sàng chinh phục phỏng vấn iOS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Profiling Voi Instruments
Viec xac dinh cac van de hieu suat doi hoi cac cong cu do luong chinh xac. Instruments cung cap mot so mau phu hop voi SwiftUI.
// Measurement points for debugging
struct PerformanceMonitor {
// Measure view creation time
static func measureViewCreation<T: View>(
_ name: String,
@ViewBuilder content: () -> T
) -> T {
let start = CFAbsoluteTimeGetCurrent()
let view = content()
let elapsed = CFAbsoluteTimeGetCurrent() - start
#if DEBUG
if elapsed > 0.016 { // More than 16ms = frame drop
print("[(name)] View creation took (elapsed * 1000)ms")
}
#endif
return view
}
}
// Extension to trace renders
extension View {
func debugRender(_ label: String) -> some View {
#if DEBUG
let _ = Self._printChanges()
print("Rendering: (label)")
#endif
return self
}
func measureRender(_ label: String) -> some View {
modifier(RenderMeasureModifier(label: label))
}
}
struct RenderMeasureModifier: ViewModifier {
let label: String
@State private var renderCount = 0
func body(content: Content) -> some View {
content
.onAppear {
renderCount += 1
#if DEBUG
print("[(label)] Render count: (renderCount)")
#endif
}
}
}Danh Sach Kiem Tra Toi Uu Hoa Voi Instruments
De profiling danh sach SwiftUI hieu qua:
- Time Profiler: xac dinh cac ham tieu thu CPU nhieu nhat
- Allocations: xac minh su tang truong bo nho trong khi cuon
- SwiftUI Instrument: truc quan hoa danh gia body
- Core Animation: phat hien rot khung hinh
// Instrumented view for profiling
struct ProfiledContactList: View {
let contacts: [Contact]
var body: some View {
let _ = Self._printChanges() // Shows changes triggering re-render
List {
ForEach(contacts) { contact in
ContactRow(contact: contact)
.measureRender("ContactRow-(contact.id)")
}
}
}
}API debugging SwiftUI nay in ra console cac thuoc tinh nao da thay doi va kich hoat viec danh gia lai body. Can thiet de xac dinh cac render lai khong can thiet.
Toi Uu Hoa Nang Cao Voi drawingGroup
Doi voi cac view phuc tap voi nhieu hieu ung hinh anh, drawingGroup() co the cai thien dang ke hieu suat bang cach rasterize view thanh mot lop Metal.
// Cell with complex visual effects
struct ComplexVisualRow: View {
let item: Item
var body: some View {
HStack(spacing: 16) {
// Circle with gradient and shadow
Circle()
.fill(
RadialGradient(
colors: [.blue, .purple, .pink],
center: .center,
startRadius: 0,
endRadius: 25
)
)
.frame(width: 50, height: 50)
.shadow(color: .purple.opacity(0.5), radius: 8, y: 4)
VStack(alignment: .leading, spacing: 4) {
Text(item.name)
.font(.headline)
// Progress bar with gradient
GeometryReader { geometry in
Capsule()
.fill(Color.gray.opacity(0.2))
.overlay(alignment: .leading) {
Capsule()
.fill(
LinearGradient(
colors: [.green, .yellow, .orange],
startPoint: .leading,
endPoint: .trailing
)
)
.frame(width: geometry.size.width * item.progress)
}
}
.frame(height: 8)
}
}
.padding()
// Rasterization for performance
.drawingGroup()
}
}
// List using optimized cells
struct OptimizedComplexList: View {
let items: [Item]
var body: some View {
ScrollView {
LazyVStack(spacing: 8) {
ForEach(items) { item in
ComplexVisualRow(item: item)
}
}
.padding()
}
}
}
struct Item: Identifiable {
let id: UUID
let name: String
let progress: Double
}drawingGroup() dac biet hieu qua cho cac view ket hop gradient, bong va hieu ung lam mo.
Nguon Tham Khao
- WWDC26 SwiftUI Guide - Tai lieu chinh thuc cua Apple ve cac cap nhat SwiftUI iOS 27 bao gom caching AsyncImage va API reorderable
- New SwiftUI APIs for Reordering and Drag and Drop on iOS 27 - Giai thich chi tiet ve modifier
.reorderable()va.reorderContainer(for:) - What's New in SwiftUI for iOS 27 - Tong quan toan dien ve cac thay doi SwiftUI WWDC26
Ket Luan
Toi uu hoa danh sach SwiftUI dua tren su hieu biet sau sac ve cac co che lazy loading, tai su dung va so sanh view. iOS 27 mang den nhung cai tien dang ke voi caching AsyncImage native va cac API swipe actions va reordering khong phu thuoc container.
Danh Sach Kiem Tra Hieu Suat SwiftUI
- Su dung
LazyVStackhoacListthay viVStackcho cac tap hop - Trien khai
Identifiablevoi ID on dinh va duy nhat - Ap dung
Equatablecho cac o phuc tap - Tan dung caching AsyncImage native cua iOS 27, hoac trien khai caching tuy chinh cho cac phien ban truoc
- Tai truoc du lieu voi prefetching thong minh
- Chon
Listde tai su dung o hoacLazyVStackcho layout tuy chinh (ca hai deu ho tro swipe actions va reordering) - Su dung
pinnedViewscho header section - Trien khai phan trang cho tap du lieu lon
- Profiling thuong xuyen voi Instruments
- Ap dung
drawingGroup()cho cac view voi hieu ung hinh anh phuc tap
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Bạn có tìm ra lỗi trong iOS không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 19 tháng 8, 2026
Thẻ
Chia sẻ
Bài viết liên quan

Việc làm lập trình viên iOS 2026: Nguồn tuyển dụng, mức lương, cách chuẩn bị phỏng vấn
Hướng dẫn toàn diện tìm việc lập trình viên iOS năm 2026. Khám phá nguồn tuyển dụng, dữ liệu lương thực tế, và chiến lược chuẩn bị phỏng vấn kỹ thuật.

ViewModifier tùy chỉnh trong SwiftUI: các mẫu tái sử dụng cho Design System
Xây dựng ViewModifier tùy chỉnh trong SwiftUI cho một design system nhất quán. Các mẫu, thực hành tốt nhất và ví dụ thực tế để tạo kiểu cho view iOS hiệu quả.

SwiftUI @Observable vs @State: Khi Nào Dùng Cái Nào Năm 2026
Nắm vững sự khác biệt giữa @Observable và @State trong SwiftUI để chọn công cụ quản lý state phù hợp cho ứng dụng iOS.