SwiftUI实现Excel风格视图的单元格可编辑功能求助
解决SwiftUI类Excel视图的单元格编辑问题
要实现单个单元格的编辑功能,核心是维护单元格数据状态并处理TextField的交互逻辑,以下是具体改造方案:
1. 定义单元格数据存储
首先需要一个二维数组保存每个单元格的内容,替换原有的静态(row, col)文本:
@State private var cellValues: [[String]] = Array(repeating: Array(repeating: "", count: 20), count: 30)
初始化时填充默认标识,保持初始显示效果与原有一致:
init() { _cellValues = State(initialValue: Array(0..<30).map { row in Array(0..<20).map { col in "(\(row), \(col))" } }) }
2. 跟踪编辑状态
添加状态变量记录当前编辑的单元格位置,以及临时绑定值处理实时输入:
@State private var editingRow: Int? @State private var editingCol: Int? @State private var tempText: String = ""
3. 改造单元格视图
将原Text替换为可交互的TextField,处理点击切换编辑、焦点管理与内容保存:
// 改造后的单元格代码 Group { if editingRow == row && editingCol == col { TextField("", text: $tempText) .frame(width: 70, height: 50) .border(Color.blue) .background(Color.yellow.opacity(0.2)) .onSubmit { cellValues[row][col] = tempText editingRow = nil editingCol = nil } .onDisappear { cellValues[row][col] = tempText } } else { Text(cellValues[row][col]) .frame(width: 70, height: 50) .border(Color.blue) .onTapGesture { tempText = cellValues[row][col] editingRow = row editingCol = col } } } .id("\(row)_\(col)")
通过条件判断切换文本显示或编辑框,点击文本激活编辑,提交/失去焦点时保存内容。
完整改造后的代码
struct ContentView: View { let columns = 20 let rows = 30 @State private var offset = CGPoint.zero // 存储单元格数据 @State private var cellValues: [[String]] // 编辑状态跟踪 @State private var editingRow: Int? @State private var editingCol: Int? @State private var tempText: String = "" init() { // 初始化单元格默认值 _cellValues = State(initialValue: Array(0..<rows).map { row in Array(0..<columns).map { col in "(\(row), \(col))" } }) } var body: some View { HStack(alignment: .top, spacing: 0) { VStack(alignment: .leading, spacing: 0) { // empty corner Color.clear.frame(width: 70, height: 50) ScrollView([.vertical]) { rowsHeader .offset(y: offset.y) } .disabled(true) } VStack(alignment: .leading, spacing: 0) { ScrollView([.horizontal]) { colsHeader .offset(x: offset.x) } .disabled(true) table .coordinateSpace(name: "scroll") } } .padding() } var colsHeader: some View { HStack(alignment: .top, spacing: 0) { ForEach(0..<columns) { col in Text("COL \(col)") .foregroundColor(.secondary) .font(.caption) .frame(width: 70, height: 50) .border(Color.blue) } } } var rowsHeader: some View { VStack(alignment: .leading, spacing: 0) { ForEach(0..<rows) { row in Text("ROW \(row)") .foregroundColor(.secondary) .font(.caption) .frame(width: 70, height: 50) .border(Color.blue) } } } var table: some View { ScrollView([.vertical, .horizontal]) { VStack(alignment: .leading, spacing: 0) { ForEach(0..<rows) { row in HStack(alignment: .top, spacing: 0) { ForEach(0..<columns) { col in // 改造后的单元格 Group { if editingRow == row && editingCol == col { TextField("", text: $tempText) .frame(width: 70, height: 50) .border(Color.blue) .background(Color.yellow.opacity(0.2)) .onSubmit { cellValues[row][col] = tempText editingRow = nil editingCol = nil } .onDisappear { cellValues[row][col] = tempText } } else { Text(cellValues[row][col]) .frame(width: 70, height: 50) .border(Color.blue) .onTapGesture { tempText = cellValues[row][col] editingRow = row editingCol = col } } } .id("\(row)_\(col)") } } } } .background( GeometryReader { geo in Color.clear .preference(key: ViewOffsetKey.self, value: geo.frame(in: .named("scroll")).origin) }) .onPreferenceChange(ViewOffsetKey.self) { value in print("offset >> \(value)") offset = value } } } } struct ViewOffsetKey: PreferenceKey { typealias Value = CGPoint static var defaultValue = CGPoint.zero static func reduce(value: inout Value, nextValue: () -> Value) { value.x += nextValue().x value.y += nextValue().y } }
关键说明
- 二维数组
cellValues持久化存储单元格内容,确保修改后数据不丢失 editingRow/editingCol控制编辑状态,同一时间仅允许编辑一个单元格- 点击文本单元格激活TextField,临时变量
tempText处理实时输入,提交/失焦时同步回数据源 - 编辑状态添加背景色区分,提升用户交互体验
内容的提问来源于stack exchange,提问作者BSR
相关产品推荐
相关产品推荐

