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

SwiftUI macOS 可双击编辑单元格的三列Table实现问题

macOS SwiftUI 三列可编辑Table实现方案(仿系统DNS样式)

问题修复与功能实现说明

1. 编辑状态TextField偏移修复

macOS下SwiftUI的TextField默认带有内边距和样式,导致和非编辑状态的Text对齐偏移。解决方案:

  • 给TextField设置textFieldStyle(.plain)移除默认样式
  • 自定义padding匹配Table单元格的默认文本边距(如EdgeInsets(top: 3, leading: 6, bottom: 3, trailing: 6))
  • 用frame(maxWidth: .infinity, alignment: .leading)确保文本左对齐,和非编辑状态一致

2. 整行高亮与加减按钮实现

  • 整行高亮:通过给Table绑定selection参数,配合TableRow的背景色自定义(或使用系统默认选中样式),实现单击选中整行并高亮
  • 加减按钮:在Table同级放置按钮组,绑定数据数组的添加/删除逻辑,删除按钮根据选中状态动态禁用

完整实现代码

import SwiftUI

struct DNSRecord: Identifiable {
    let id = UUID()
    var type: String
    var server: String
    var port: String
}

struct DNSTableView: View {
    @State private var records: [DNSRecord] = [
        DNSRecord(type: "IPv4", server: "8.8.8.8", port: "53"),
        DNSRecord(type: "IPv6", server: "2001:4860:4860::8888", port: "53")
    ]
    @State private var selectedRecordId: DNSRecord.ID?
    @State private var editingCell: (DNSRecord.ID, String)? // 记录当前编辑的单元格(ID+字段名)
    
    var body: some View {
        VStack(spacing: 12) {
            Table(records, selection: $selectedRecordId) {
                TableColumn("类型") { record in
                    cellContent(for: record, field: "type", value: record.type)
                }
                TableColumn("服务器") { record in
                    cellContent(for: record, field: "server", value: record.server)
                }
                TableColumn("端口") { record in
                    cellContent(for: record, field: "port", value: record.port)
                }
            }
            .tableStyle(.bordered)
            
            HStack(spacing: 16) {
                Button(action: addNewRecord) {
                    Image(systemName: "plus")
                }
                Button(action: deleteSelectedRecord) {
                    Image(systemName: "minus")
                }
                .disabled(selectedRecordId == nil)
            }
            .buttonStyle(.bordered)
        }
        .padding()
        .frame(minWidth: 600, minHeight: 400)
    }
    
    // 复用单元格内容视图,处理编辑/非编辑状态
    private func cellContent(for record: DNSRecord, field: String, value: String) -> some View {
        Group {
            if editingCell == (record.id, field) {
                TextField("", text: Binding(
                    get: { value },
                    set: { newValue in
                        updateRecord(record.id, field: field, value: newValue)
                    }
                ))
                .textFieldStyle(.plain)
                .padding(EdgeInsets(top: 3, leading: 6, bottom: 3, trailing: 6))
                .onSubmit { editingCell = nil }
                .onExitCommand { editingCell = nil }
            } else {
                Text(value)
                    .padding(EdgeInsets(top: 3, leading: 6, bottom: 3, trailing: 6))
                    .onTapGesture(count: 2) {
                        editingCell = (record.id, field)
                    }
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }
    
    // 更新指定记录的字段值
    private func updateRecord(_ id: DNSRecord.ID, field: String, value: String) {
        guard let index = records.firstIndex(where: { $0.id == id }) else { return }
        var record = records[index]
        switch field {
        case "type": record.type = value
        case "server": record.server = value
        case "port": record.port = value
        default: break
        }
        records[index] = record
    }
    
    // 添加新记录
    private func addNewRecord() {
        records.append(DNSRecord(type: "", server: "", port: "53"))
    }
    
    // 删除选中记录
    private func deleteSelectedRecord() {
        guard let selectedId = selectedRecordId else { return }
        records.removeAll { $0.id == selectedId }
        selectedRecordId = nil
    }
}

struct DNSTableView_Previews: PreviewProvider {
    static var previews: some View {
        DNSTableView()
    }
}

代码关键点说明

  • 用editingCell元组精准控制单个单元格的编辑状态,双击单元格触发编辑
  • TextField通过plain样式+自定义padding完全匹配非编辑状态的文本位置,解决偏移问题
  • Table绑定selection实现单击选中整行,系统默认提供高亮样式(可通过background修饰符自定义)
  • 加减按钮直接操作数据数组,删除按钮根据选中状态动态禁用,符合系统设置的交互逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:25:09