SwiftUI开发Mac仿iOS电池小组件运行后显示空白窗口,求问题排查
问题排查与修复方案
核心问题
- 初始化的
PublishedDevices实例deviceArrays始终为空,你从未将传入的devices数组拆分后赋值给它,ForEach遍历空数组自然无内容渲染,是空白页的直接原因 - 用单状态变量
currentDevice绑定所有Progresso的device参数,会导致所有卡片只能显示同一个设备的信息,逻辑错误 - 仅计算了
devicesPerRow、rows参数,没有实际用来拆分设备数组生成对应的行数据,计算逻辑完全无效 - GeometryReader默认撑满父容器,但内部内容无明确高度约束,外层ScrollView无法正确计算内容高度,也会导致内容无法正常显示
修复后的BatteryGrid.swift代码
struct BatteryGrid: View { @Binding var devices: [Device] @Binding var size: Double @ObservedObject var deviceArrays = PublishedDevices() @State var devicesPerRow = 0 var body: some View { VStack(spacing: 20) { // 只用来获取父容器宽度,不占实际显示空间 GeometryReader { geometry in Color.clear .onAppear { devicesPerRow = Int(geometry.size.width) / Int(size) splitDevicesToRows() } } .frame(height: 0) // 生成每一行 ForEach(deviceArrays.devices, id: \.id) { deviceArray in HStack(spacing: 10) { // 生成当前行的所有设备卡片 ForEach(deviceArray.row, id: \.id) { device in Progresso(device: .constant(device), size: $size) .frame(width: size) } // 不足一行的位置补空白占位,保证布局对齐 ForEach(0..<(devicesPerRow - deviceArray.row.count), id: \.self) { _ in Spacer() .frame(width: size) } } } } .padding() } /// 将一维设备数组拆分为多行 private func splitDevicesToRows() { guard devicesPerRow > 0 else { return } var tempRows: [DeviceRow] = [] var currentRow: [Device] = [] for device in devices { currentRow.append(device) if currentRow.count == devicesPerRow { tempRows.append(DeviceRow(row: currentRow)) currentRow.removeAll() } } if !currentRow.isEmpty { tempRows.append(DeviceRow(row: currentRow)) } deviceArrays.devices = tempRows } } struct DeviceRow: Identifiable { var row: [Device] = [] var id = UUID() } class PublishedDevices: ObservableObject { @Published var devices: [DeviceRow] = [] }
额外注意事项
- 确认
Device结构体遵循Identifiable协议,存在唯一的id属性,否则ForEach遍历会崩溃 - 确认
Device的icon()方法已正确实现,返回对应设备类型的Image实例 - 如果需要支持设备列表动态更新,可以新增
onChange(of: devices)modifier,监听到设备列表变化时重新调用splitDevicesToRows()更新布局
内容的提问来源于stack exchange,提问作者Mark Reggiardo
相关产品推荐
相关产品推荐

