SwiftUI像素画编辑器切换网格数据丢失问题求助
解决SwiftUI像素画编辑器网格切换数据丢失问题
核心问题在于你没有为每个切片(网格)维护独立的绘制数据,切换时会重新初始化空白数据。以下是实现32个切片并保留数据的具体方案:
1. 定义独立的网格数据模型
创建一个结构体存储单个网格的像素数据,确保每个切片的数据相互独立:
struct PixelGrid: Identifiable { let id = UUID() var pixels: [[Color]] // 存储每个像素的颜色,可根据需求替换为自定义颜色类型 let size: Int // 网格尺寸,比如16x16 // 初始化空白网格 init(size: Int) { self.size = size self.pixels = Array(repeating: Array(repeating: .white, count: size), count: size) } }
2. 用ViewModel管理所有切片数据
通过ViewModel维护32个网格实例,同时追踪当前选中的切片索引:
class PixelEditorViewModel: ObservableObject { @Published var grids: [PixelGrid] @Published var selectedGridIndex = 0 // 初始化32个空白网格 init(gridSize: Int) { self.grids = Array(repeating: PixelGrid(size: gridSize), count: 32) } // 便捷访问当前选中的网格 var currentGrid: PixelGrid { get { grids[selectedGridIndex] } set { grids[selectedGridIndex] = newValue } } }
3. 在View中绑定数据并实现切换逻辑
在主视图中添加切片选择控件,绑定ViewModel的索引,画布直接使用当前选中的网格数据:
struct PixelEditorView: View { @StateObject private var viewModel = PixelEditorViewModel(gridSize: 16) var body: some View { VStack { // 切片选择器,支持切换32个网格 Picker("选择切片", selection: $viewModel.selectedGridIndex) { ForEach(0..<32, id: \.self) { index in Text("切片 \(index + 1)").tag(index) } } .pickerStyle(.segmented) .padding() // 像素绘制画布,绑定当前选中的网格 PixelCanvas(grid: $viewModel.currentGrid) } } } // 示例画布组件,处理像素绘制交互 struct PixelCanvas: View { @Binding var grid: PixelGrid var body: some View { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: grid.size)) { ForEach(0..<grid.size, id: \.self) { row in ForEach(0..<grid.size, id: \.self) { col in Rectangle() .fill(grid.pixels[row][col]) .border(.gray) .onTapGesture { // 示例:点击切换黑白,可替换为自定义颜色选择逻辑 grid.pixels[row][col] = grid.pixels[row][col] == .white ? .black : .white } } } } .padding() } }
4. 可选:添加持久化支持
如果需要重启APP后保留绘制数据,让PixelGrid实现Codable,通过UserDefaults或文件存储保存所有网格数据:
extension PixelGrid: Codable { enum CodingKeys: String, CodingKey { case pixels, size } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(size, forKey: .size) // 将Color转为Hex字符串存储 let pixelHexes = pixels.map { $0.map { $0.toHexString() } } try container.encode(pixelHexes, forKey: .pixels) } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) size = try container.decode(Int.self, forKey: .size) let pixelHexes = try container.decode([[String]].self, forKey: .pixels) pixels = pixelHexes.map { $0.map { Color(hex: $0) ?? .white } } } } // 扩展Color实现Hex转换 extension Color { func toHexString() -> String { let uiColor = UIColor(self) var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) return String(format: "#%02lX%02lX%02lX", lroundf(Float(r*255)), lroundf(Float(g*255)), lroundf(Float(b*255))) } init?(hex: String) { let cleanHex = hex.trimmingCharacters(in: .alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: cleanHex).scanHexInt64(&int) let a, r, g, b: UInt64 switch cleanHex.count { case 3: (a, r, g, b) = (255, (int>>8)*17, (int>>4&0xF)*17, (int&0xF)*17) case 6: (a, r, g, b) = (255, int>>16, int>>8&0xFF, int&0xFF) case 8: (a, r, g, b) = (int>>24, int>>16&0xFF, int>>8&0xFF, int&0xFF) default: return nil } self.init(red: Double(r)/255, green: Double(g)/255, blue: Double(b)/255, opacity: Double(a)/255) } }
这样实现后,每个切片的绘制数据都会独立存储,切换时不会丢失原有内容,完全满足32个切片的需求。
内容的提问来源于stack exchange,提问作者Edward Taylor
相关产品推荐
相关产品推荐

