侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现高性能可过滤列表视图与自定义异步图片加载

2025-12-11 / 0 评论 / 4 阅读

题目

实现高性能可过滤列表视图与自定义异步图片加载

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

SwiftUI性能优化,Combine框架应用,自定义视图与布局,状态管理,异步数据处理

快速回答

实现高性能SwiftUI列表需要:

  • 使用LazyVStackList配合ForEach实现视图重用
  • 通过@ViewBuilder构建自定义单元格视图
  • 采用CombineDebounceRemoveDuplicates优化搜索过滤
  • 使用Task@State管理异步图片加载状态
  • 实现Equatable协议减少无效刷新
## 解析

问题场景

在SwiftUI中处理10000+条数据列表,要求:1) 实现实时搜索过滤 2) 自定义单元格布局 3) 异步加载网络图片 4) 滚动流畅不卡顿。

核心解决方案

1. 数据结构与状态管理

struct Product: Identifiable, Equatable {
    let id: UUID
    var name: String
    var imageURL: URL?

    // 实现Equatable减少无效刷新
    static func == (lhs: Self, rhs: Self) -> Bool {
        lhs.id == rhs.id && lhs.name == rhs.name
    }
}

class ProductViewModel: ObservableObject {
    @Published var searchText = ""
    @Published private(set) var filteredProducts: [Product] = []
    private var allProducts: [Product] = []

    private var cancellables = Set<AnyCancellable>()

    init() {
        // 模拟初始化10000条数据
        allProducts = (0..<10000).map {
            Product(id: UUID(), name: "Product \($0)", imageURL: URL(string: "https://picsum.photos/200?random=\($0)"))
        }

        // Combine处理搜索过滤(带防抖)
        $searchText
            .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
            .removeDuplicates()
            .map { [allProducts] text in
                text.isEmpty ? allProducts : allProducts.filter { $0.name.contains(text) }
            }
            .assign(to: &$filteredProducts)
    }
}

2. 列表视图优化实现

struct ProductListView: View {
    @StateObject var viewModel = ProductViewModel()

    var body: some View {
        VStack {
            // 搜索框
            TextField("Search...", text: $viewModel.searchText)
                .padding()

            // 使用LazyVStack优化性能
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(viewModel.filteredProducts) { product in
                        ProductRow(product: product)
                            .padding(.vertical, 8)
                    }
                }
            }
        }
    }
}

// 自定义单元格视图
struct ProductRow: View {
    let product: Product

    var body: some View {
        HStack {
            // 异步图片加载视图
            AsyncImageView(url: product.imageURL)
                .frame(width: 60, height: 60)

            Text(product.name)
                .font(.headline)

            Spacer()
        }
        .padding(.horizontal)
    }
}

3. 异步图片加载实现

struct AsyncImageView: View {
    let url: URL?
    @State private var image: UIImage?
    @State private var isLoading = false

    var body: some View {
        Group {
            if let image = image {
                Image(uiImage: image)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } else if isLoading {
                ProgressView()
            } else {
                Image(systemName: "photo")
            }
        }
        .task {
            await loadImage()
        }
    }

    private func loadImage() async {
        guard let url = url, image == nil else { return }
        isLoading = true

        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            if let loadedImage = UIImage(data: data) {
                // 主线程更新UI
                await MainActor.run {
                    image = loadedImage
                }
            }
        } catch {
            print("Image loading failed: \(error)")
        }

        isLoading = false
    }
}

最佳实践

  • 性能优化:优先使用LazyVStack/LazyHStack替代List处理超大数据集
  • 状态管理@StateObject管理ViewModel生命周期,@Published驱动UI更新
  • 异步处理:使用Task配合async/await管理异步操作,避免回调地狱
  • 内存优化:图片加载实现内存缓存(可扩展为NSCache

常见错误

  • 直接使用List+大量数据:未实现单元格复用导致内存暴涨
  • 未防抖的实时搜索:每次输入都触发过滤导致性能卡顿
  • 主线程加载图片:阻塞UI导致滚动卡顿
  • 未实现Equatable:数据微变化时引发整个列表刷新

扩展知识

  • 高级优化:实现onAppear/onDisappear控制图片加载时机
  • 依赖注入:将ImageLoader作为环境对象注入
  • 测试策略:使用XCTest测试ViewModel逻辑,ViewInspector测试UI状态
  • 替代方案:对于超大数据集(10万+),考虑使用CoreDataRealm的懒加载