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

SwiftUI中如何持久化保存拖拽后餐桌图形的位置?

在SwiftUI中持久化餐桌位置的解决方案

要解决关闭应用后餐桌位置重置的问题,核心是持久化每个餐桌的位置数据,下面是具体实现步骤和代码示例:

1. 定义可序列化的餐桌模型

首先创建一个符合Identifiable和Codable的Table结构体,用来存储餐桌的ID、位置和尺寸:

// 扩展CGPoint使其支持Codable(Swift默认不支持)
extension CGPoint: Codable {
    enum CodingKeys: String, CodingKey {
        case x, y
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(x, forKey: .x)
        try container.encode(y, forKey: .y)
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        x = try container.decode(CGFloat.self, forKey: .x)
        y = try container.decode(CGFloat.self, forKey: .y)
    }
}

struct Table: Identifiable, Codable {
    let id = UUID()
    var position: CGPoint
    let size: CGSize = CGSize(width: 80, height: 80) // 可根据需求改为可配置属性
}

2. 创建数据管理类

用ObservableObject实现一个TableManager,负责餐桌数据的增删改、持久化存储和加载:

class TableManager: ObservableObject {
    @Published var tables: [Table] = []
    private let defaultsKey = "RestaurantSavedTables"
    
    // 初始化时加载已保存的数据
    init() {
        loadSavedTables()
    }
    
    // 从UserDefaults加载数据
    private func loadSavedTables() {
        guard let data = UserDefaults.standard.data(forKey: defaultsKey),
              let savedTables = try? JSONDecoder().decode([Table].self, from: data) else {
            return
        }
        tables = savedTables
    }
    
    // 保存数据到UserDefaults
    func saveTables() {
        guard let data = try? JSONEncoder().encode(tables) else {
            return
        }
        UserDefaults.standard.set(data, forKey: defaultsKey)
    }
    
    // 添加新餐桌
    func addTable(at position: CGPoint) {
        tables.append(Table(position: position))
        saveTables()
    }
    
    // 更新餐桌位置
    func updateTablePosition(for table: Table, newPosition: CGPoint) {
        guard let index = tables.firstIndex(where: { $0.id == table.id }) else {
            return
        }
        tables[index].position = newPosition
        saveTables()
    }
}

3. 实现视图与拖拽交互

在主视图中使用TableManager,绑定拖拽手势更新位置并触发保存:

struct RestaurantPlanView: View {
    @StateObject private var tableManager = TableManager()
    
    var body: some View {
        ZStack {
            // 餐厅平面图背景(示例)
            Color.gray.opacity(0.1)
                .ignoresSafeArea()
            
            // 渲染所有餐桌
            ForEach($tableManager.tables) { $table in
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: table.size.width, height: table.size.height)
                    .position(table.position)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                // 拖拽过程中实时更新位置
                                table.position = value.location
                            }
                            .onEnded { _ in
                                // 拖拽结束后保存所有数据
                                tableManager.saveTables()
                            }
                    )
            }
            
            // 添加餐桌按钮(示例:点击在屏幕中央添加)
            Button("添加餐桌") {
                let centerPos = CGPoint(
                    x: UIScreen.main.bounds.midX,
                    y: UIScreen.main.bounds.midY
                )
                tableManager.addTable(at: centerPos)
            }
            .position(x: 80, y: 80)
            .buttonStyle(.borderedProminent)
        }
    }
}

补充说明

  • 如果你的应用需要管理大量餐桌或更复杂的属性(如餐桌编号、座位数),建议用Core Data替代UserDefaults,避免存储大量数据的性能问题。
  • 拖拽时的保存时机可以优化:比如用debounce减少保存频率,避免频繁写入存储,但简单场景下拖拽结束后直接保存足够实用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 21:21:11