SwiftUI基于Stand模型创建可独立绑定数组的实现求助
问题解决:独立绑定Stand模型并实现面积计算
问题说明
列表项数量由用户输入的“Number of Stands”决定,但原代码中所有StandRowView绑定同一个speed变量,导致输入速度时所有行同步变化。需实现:
- 每行数据与
Stand模型关联,实现独立数据绑定 - 通过
.onChange监听数据变化,计算相邻Stand的面积比值并更新右侧文本
修改后的完整代码
1. Stand 模型定义
调整属性类型以支持计算,添加动态初始化方法:
import Foundation import SwiftUI struct Stand: Codable, Identifiable, Hashable { var id: String { stand } var stand: String var area: Double // 改为Double类型方便数值计算 var speed: Int var rf: Double // 存储当前Stand与下一个Stand的面积比值 // 根据工位序号和初始面积创建实例 init(standNumber: Int, initialArea: Double) { self.stand = "\(standNumber)" self.area = initialArea self.speed = 0 self.rf = 0.0 } }
2. StandRowView 行视图
直接绑定整个Stand对象,实现独立数据管理:
import SwiftUI struct StandRowView: View { @Binding var stand: Stand var body: some View { HStack { Text("Stand") Text(stand.stand) TextField("Enter Speed", value: $stand.speed, format: .number) .frame(width: 110, height: 40) .foregroundColor(.black) .multilineTextAlignment(.center) .background(Color.white) .border(Color.gray, width: 1) .cornerRadius(10) .padding(.trailing, 30) // 显示计算后的面积比值,保留两位小数 Text(String(format: "%.2f", stand.rf)) } .padding() } }
3. Speeds1View 主视图
使用数组存储所有Stand数据,动态创建列表项,自动计算面积比值:
import SwiftUI struct Speeds1View: View { let title: String let productArea: Double let noStands: Int let billetArea: Double @Environment(\.dismiss) var dismiss @State private var stands: [Stand] = [] // 计算总压缩比 private var reductionRatio: Double { billetArea / productArea } // 计算平均面积压缩率 private var averageAreaReduction: Double { let ratioPerStand = pow(reductionRatio, 1.0 / Double(noStands)) return (1 - 1/ratioPerStand) * 100 } var body: some View { NavigationStack { List { // 顶部信息栏 VStack(spacing: 8) { Text("# of Stands") Text("\(noStands)") Text("Billet Area") Text(String(format: "%.2f", billetArea)) Text("Product Area") Text(String(format: "%.2f", productArea)) Text("Reduction Ratio: \(String(format: "%.2f", reductionRatio)) : 1") Text("Ave. Area Red: \(String(format: "%.1f", averageAreaReduction))%") } .frame(maxWidth: .infinity) .padding(.vertical) // 动态生成Stand行 ForEach($stands) { $stand in StandRowView(stand: $stand) .listRowInsets(EdgeInsets()) .listRowSeparator(.hidden) } } .onAppear { // 初始化Stand数组,按等比例压缩分配初始面积 let initialAreas = calculateInitialAreas() stands = (1...noStands).enumerated().map { index, number in Stand(standNumber: number, initialArea: initialAreas[index]) } // 首次计算面积比值 calculateRatios() } .onChange(of: stands) { _, _ in // 任何Stand数据变化时,重新计算比值 calculateRatios() } .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(action: dismiss) { Image(systemName: "arrowshape.turn.up.backward") .font(.footnote) .foregroundColor(.blue) } } ToolbarItem(placement: .navigationBarTrailing) { Button(action: { // 可添加保存/导出逻辑 }) { Image(systemName: "square.and.arrow.up") .resizable() .frame(width: 20, height: 25) .foregroundStyle(Color.blue, Color.gray) } } } .navigationBarTitleDisplayMode(.inline) .navigationTitle("Calculate Area, \(title)") } } // 按等比例压缩逻辑计算每个Stand的初始面积 private func calculateInitialAreas() -> [Double] { var areas: [Double] = [] var currentArea = billetArea let ratioPerStand = pow(reductionRatio, 1.0 / Double(noStands)) for _ in 1...noStands { areas.append(currentArea) currentArea /= ratioPerStand } // 确保最后一个Stand的面积与产品面积一致 if !areas.isEmpty { areas[areas.count - 1] = productArea } return areas } // 计算每个Stand与下一个Stand的面积比值 private func calculateRatios() { for index in 0..<stands.count { if index < stands.count - 1 { stands[index].rf = stands[index].area / stands[index + 1].area } else { // 最后一个Stand无后续工位,比值设为1.0 stands[index].rf = 1.0 } } } }
核心修改说明
- 用
@State [Stand]数组替代单个变量,实现每行数据独立存储 StandRowView直接绑定Stand对象,确保输入操作只影响当前行- 通过
.onAppear和.onChange自动触发面积比值计算,实时更新UI - 调整模型属性类型为数值型,方便后续计算逻辑扩展
内容的提问来源于stack exchange,提问作者johnny
相关产品推荐
相关产品推荐

