You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

M1 iPad Pro上LazyVGrid无限滚动顶部项消失及崩溃问题求助

SwiftUI LazyVGrid无限滚动在M1 iPad Pro上的崩溃问题分析

编辑说明:该问题在iOS 17.2后似乎已消失。

问题现象

使用SwiftUI的LazyVGrid结合ScrollView实现无限滚动视图时,当滚动到1500+项的深度后,点击回到顶部,列表前几项会消失;此时尝试向上滚动,应用直接崩溃。该问题仅在M1 iPad Pro物理设备上出现,iPhone 15 Pro或Xcode模拟器中无此现象。

最简复现代码

ContentView.swift

import SwiftUI

struct ContentView: View {
    @State var titles = [String]()
    
    let columns = [
        GridItem(.adaptive(minimum: 160), spacing: 20, alignment: .top)
    ]
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns) {
                ForEach(titles, id: \.self) { title in
                    ChildView(title: title)
                        .onAppear {
                            if title == titles.last {
                                let randomInt = Int.random(in: 0..<100)
                                let result = (titles.count...titles.count+randomInt)
                                    .map { value in
                                        String(value)
                                    }
                                titles.append(contentsOf: result)
                            }
                        }
                }
            }
        }
        .onAppear {
            let randomInt = Int.random(in: 0..<100)
            let result = (titles.count...titles.count+randomInt)
                .map { value in
                    String(value)
                }
            titles.append(contentsOf: result)
        }
    }
}

ChildView.swift

import SwiftUI

struct ChildView: View {
    let title: String
    var body: some View {
        Text(title)
            .frame(height: 100)
    }
}

可能原因分析

  1. SwiftUI框架版本bug:iOS 17.2已修复该问题,说明是Apple在SwiftUI懒加载视图渲染、内存管理逻辑上的漏洞,且仅在M1 iPad Pro的硬件/渲染环境下触发边界条件。
  2. 视图标识稳定性不足:使用id: \.self以字符串作为视图唯一标识,虽然当前逻辑下字符串不会重复,但大量数据下SwiftUI的哈希计算或视图复用机制可能出现异常,导致回滚时无法正确重建已回收的视图。
  3. 状态更新时机冲突:快速滚动回顶部时,多个视图的onAppear可能同时触发,短时间内频繁修改titles数组,引发视图状态与数据源的不一致,进而导致渲染崩溃。
  4. M1设备特定渲染逻辑:M1芯片的多线程渲染、内存分页机制与SwiftUI的懒加载逻辑交互时出现异常,模拟器和iPhone的硬件环境未触发该边界条件。

修复方案

临时兼容方案(iOS 17.2以下)

  1. 使用稳定的视图标识:将数据源改为带唯一ID的结构体,避免依赖字符串哈希作为标识:
    struct GridItemModel: Identifiable {
        let id = UUID()
        let title: String
    }
    
    // 在ContentView中修改状态
    @State var items = [GridItemModel]()
    
    // LazyVGrid中的ForEach
    ForEach(items) { item in
        ChildView(title: item.title)
            .onAppear {
                if item.id == items.last?.id {
                    // 加载逻辑不变,改为生成GridItemModel
                    let randomInt = Int.random(in: 0..<100)
                    let newItems = (items.count...items.count+randomInt)
                        .map { GridItemModel(title: String($0)) }
                    items.append(contentsOf: newItems)
                }
            }
    }
    
  2. 优化加载触发逻辑:添加防抖或用Task异步更新状态,避免同步修改导致的状态混乱:
    .onAppear {
        guard item.id == items.last?.id else { return }
        Task { @MainActor in
            // 添加短暂延迟避免频繁触发
            try? await Task.sleep(nanoseconds: 200_000_000)
            let randomInt = Int.random(in: 0..<100)
            let newItems = (items.count...items.count+randomInt)
                .map { GridItemModel(title: String($0)) }
            items.append(contentsOf: newItems)
        }
    }
    
  3. 使用iOS17+滚动API:用ScrollViewReader和scrollPosition控制滚动,提升滚动稳定性:
    @State private var scrollID: GridItemModel.ID?
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVGrid(columns: columns) {
                    // ... 视图内容
                }
                .scrollTargetLayout()
            }
            .scrollPosition(id: $scrollID)
            // 回到顶部时调用 proxy.scrollTo(items.first?.id)
        }
    }
    

长期方案

直接升级设备系统到iOS 17.2及以上版本,Apple已在该版本中修复了此问题。

内容的提问来源于stack exchange,提问作者Doraemoe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:30:32