hi all, I'm writing to DevForum after few days of struggle 13-15h everyday trying to dig many forum for solution ( try Github for: do to list and many other app built in to switch between toggle and button to see if the issues was coming from here
My App idea seems simple actually very complex to build for me, I have manage to build the solution for one row and it's working but when it goes to adding the row into a list I'm giving up.... I do not understand why in my TimeKeyInRow structure it's working I can clic and get feedback of every single hours but when I add row to a list even if I have already created an ID it's still taking the all row as one isSet
import SwiftUI
struct HourButton: View,Identifiable {
var id: String = "hour"
@State var isSet: Bool
var value: Double = 1
var body: some View {
HStack {
Button {
isSet.toggle()
print("clic already \(isSet) \(id)h & \(value) has Value")
} label: {
Label("8h", systemImage: isSet ? "circle.fill" : "circle")
.labelStyle(.iconOnly)
.foregroundStyle(isSet ? .blue : .gray)
}.id(id)
}
}
}
struct HourButton_Previews: PreviewProvider {
static var previews: some View {
//#Preview {
HourButton(id: "MORN", isSet: true, value: 3)
}
}
This is the Row Part which is working fine
import SwiftUI
struct TimeKeyInRow: View,Identifiable {
var id: String
var project: Project
@Binding var isSet: Bool
let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
var body: some View {
HStack {
Text(project.shortname)
Spacer()
HourButton(id: "\(project.shortname)\(0)", isSet: true, value: listOfPossibleHours[0])
HourButton(id: "\(project.shortname)\(1)", isSet: true, value: listOfPossibleHours[1])
HourButton(id: "\(project.shortname)\(2)", isSet: true, value: listOfPossibleHours[2])
HourButton(id: "\(project.shortname)\(3)", isSet: true, value: listOfPossibleHours[3])
HourButton(id: "\(project.shortname)\(4)", isSet: true, value: listOfPossibleHours[4])
Text("\(project.leftPMtime)")
}
}
}
struct TimeKeyInRow_Previews: PreviewProvider {
static var previews: some View {
//#Preview {
let projects = ModelData().projects
TimeKeyInRow(id: "test", project: projects[2], isSet: .constant(true))
}
}
Then the part were the issues is coming
import SwiftUI
struct TimeKeyinList: View, Identifiable{
var id: String
@Environment(ModelData.self) var modelData
let projects = ModelData().projects
@State var isSet: Bool
@State private var hideTeco = false
var filteredProjects: [Project] {
// removeTECO project
modelData.projects.filter { project in
!project.isTeco
}}
var body: some View {
VStack {
HStack {
Text("TIME KEY_IN")
.font(.title)
.multilineTextAlignment(.leading)
.bold()
Spacer()
Text("8h")
.font(.title)
.multilineTextAlignment(.leading)
.bold()
}.padding()
List {
HStack(alignment: .top) {
Text("Project Name")
.frame(width: 80, height: 25)
.font(.caption)
.multilineTextAlignment(.center)
.lineLimit(8)
.allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
.multilineTextAlignment(.center)
.bold()
Spacer(minLength:30)
Text("0.5h")
.font(.footnote)
Text("1h")
.font(.footnote)
.padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
Text("2h")
.font(.footnote)
.padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
Text("3h")
.font(.footnote)
.padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
Text("4h")
.font(.footnote)
.padding(/*@START_MENU_TOKEN@*/EdgeInsets()/*@END_MENU_TOKEN@*/)
Text("Time Left")
.frame(width: 60, height: 25)
.font(.caption)
.multilineTextAlignment(.center)
.lineLimit(8)
.allowsTightening(/*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
.multilineTextAlignment(.center)
.bold()
}
ForEach (filteredProjects) { project in
TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet)
}
Divider()
// TimeKeyInRow(project: "TRAINING", isSet: $isSet)
// TimeKeyInRow(isSet: $isSet, projectshortname: "MEETING", timeleft: 0)
// TimeKeyInRow(isSet: $isSet, projectshortname: "NON ALLOCATED", timeleft:0)
// TimeKeyInRow(isSet: $isSet, projectshortname: "HOLIDAY", timeleft:0)
//
// }
// .listStyle(.inset)
// DatePicker(selection: /*@START_MENU_TOKEN@*/.constant(Date())/*@END_MENU_TOKEN@*/, label: { /*@START_MENU_TOKEN@*/Text("Date")/*@END_MENU_TOKEN@*/ })
}.labelsHidden()
}
}
}
struct TimeKeyinList_Previews: PreviewProvider {
static var previews: some View {
//#Preview {
TimeKeyinList(id: "test", isSet: true).environment(ModelData())
}
}
You do not show how you initialise
@State var isSet: [Bool]
Why do you repeat projects initialisation in TimeKeyInRow and in TimeKeyinList ? If I understand your intent, you should only pass a single project.
So, I have added an initialisation in .onAppear. This is just a quick test. Of course, you have to update isBool each time a isTeco toggles or each time you add a new Project.
That's just a quick fix, to show the principle.
extension String { // To align text
func paddedToWidth(_ width: Int) -> String {
let length = self.count
guard length < width else {
return self
}
let spaces = Array<Character>.init(repeating: " ", count: width - length)
return self + spaces
}
}
struct TimeKeyInRow: View,Identifiable {
var id: String
var project: Project. // Just pass the project
/* Do not redeclare
var projects = [
Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
] */
@Binding var isSet: Bool
let listOfPossibleHours: [Double] = [0.5,1,2,3,4,8]
var body: some View {
HStack {
Spacer() // ADDED
Text(project.shortname.paddedToWidth(5)) // Text(projects[1].shortname)
.font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
Spacer()
ForEach(listOfPossibleHours, id: \.self) { hour in
HourButton(id: "\(project.shortname)", isSet: isSet, value: hour)
}
/* replace this
HourButton(id: "\(project.shortname)\(0)", isSet: isSet, value: listOfPossibleHours[0])
HourButton(id: "\(project.shortname)\(1)", isSet: isSet, value: listOfPossibleHours[1])
// What do you expect with \(0)
*/
Text("\(project.leftPMtime)")
.font(.custom("Menlo", size: 16)) // <<-- To get proper alignment
Spacer() // <<-- ADDED
}
}
}
struct HourButton: View,Identifiable {
var id: String = "hour"
@State var isSet: Bool
var value: Double = 1
var body: some View {
HStack {
Button {
isSet.toggle()
print("clic already \(isSet) \(id)h & \(value) has Value")
} label: {
Label("8h", systemImage: isSet ? "circle.fill" : "circle")
.labelStyle(.iconOnly)
.foregroundStyle(isSet ? .blue : .gray)
}.id(id)
}
}
}
struct TimeKeyinList: View, Identifiable {
var id: String = "" // to ease test
var projects = [
Project(name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false),
Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
Project(name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true),
Project(name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false)
]
@State var isSet: [Bool] = [true, false, false, true]
@State private var hideTeco = false
var filteredProjects: [Project] {
// removeTECO project
projects.filter { project in
!project.isTeco
}}
var body: some View {
VStack {
ForEach(Array(filteredProjects.enumerated()), id: \.offset) { (row, project) in
TimeKeyInRow(id: project.shortname, project: project, isSet: $isSet[row])
}
}
.labelsHidden()
.onAppear() { // <<-- Added
for (row, project) in filteredProjects.enumerated() {
isSet[row] = !project.isTeco
}
}
}
}
struct Project: Identifiable {
var id: UUID = UUID()
var name: String
var shortname: String
var leftPMtime: Int
var isTeco: Bool
}
Note: What is isTeco meaning ?
In some cases, neither shortName not isTeco match with label:
Project(name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true),
Is it on purpose ?