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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:45:29