Post

Replies

Boosts

Views

Activity

Reply to Using #available(iOS 17, *) for Swift Student Challenge
That's what's happening everyday in many apps. And it is recommended by Apple to adopt most recent features. Eligibility criteria state: Your app playground must be built with and run on Swift Playgrounds 4.4 or later (requires iPadOS 16 or macOS 13.5, or later) or Xcode 15 on macOS 13.5, or later. You may incorporate the use of Apple Pencil. So you are safe with iOS 16 (I even assume they will test on iOS 17). So this approach to take profit of most recent API whilst maintaining compatibility with previous releases is OK. I could not see any reason for different conclusion. Good luck.
Feb ’24
Reply to Plain Segmented Control
It seems that the color are siuperposed. I mean, .red makes a red background, but .white does not change anything, just as if it was adding composing defaut gray color with the one used for background.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Feb ’24
Reply to Expression not allowed at top level
What you've done wrong (i mean not compliant with Swift compiler rules) is to call a func at the top level, not inside a func or a view. When you call in a view, don't call direcly in the body, but ffor instance in a button action: You should for instance call it in ContentView: struct ContentView: View { var body: some View { Text("Hello, world!") Button { calculateATip(amount: 120) } label: { Text("Calculate") } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Feb ’24
Reply to explanation of the following code step by step
If you post an email address, we shall be able to share complete project. Not waiting for this, I have looked again at the code and improved my quick and dirty changes ! No need in fact to pass projects and projectNum, as long as Bindings are correctly defined. here it is: extension String { 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 } } let allHoursFalse = [false, false, false, false, false, false] // To simplify code writing, we hard code: there 6 hours defined struct TimeKeyinList: View { // }, Identifiable { @State var projects = [Project] () // Now need a State var to be able to modify // Not the correct var @State var isSet: [Bool] = [true, false, false, true] @State private var hideTeco = false @State var filteredProjects: [Project] = []/ var body: some View { VStack(spacing: 10) { ForEach($filteredProjects, id: \.id) { $project in TimeKeyInRow(id: project.shortname, project: $project) } } .labelsHidden() .onAppear() { // <<-- Added. All hours initialised as falseall projects = [ Project(id: 1, name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false, isHourSet: allHoursFalse), Project(id: 2, name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true, isHourSet: allHoursFalse), Project(id: 3, name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true, isHourSet: allHoursFalse), Project(id: 4, name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false, isHourSet: allHoursFalse) ] filteredProjects = projects.filter { project in !project.isTeco } // for (row, project) in filteredProjects.enumerated() { // Replaced by array of Bool for hours in each project // isSet[row] = !project.isTeco // I do not understand what was isSet used for here. // } } } } struct Project: Identifiable { var id: Int = 0// UUID = UUID() var name: String var shortname: String var leftPMtime: Int var isTeco: Bool var isHourSet: [Bool] // ### To store for each hour of the project } struct TimeKeyInRow: View,Identifiable { var id: String @Binding var project: Project // <<-- Just pass the project itsel projects: [Project] // @Binding var isSet: Bool ### No need, it is in project 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(Array(listOfPossibleHours.enumerated()), id: \.offset) { (index, hour) in HourButton(id: "\(project.shortname)", isSet: $project.isHourSet[index], value: hour) // HourButton has changed, to pas values of isSet for each hour } Text("\(project.leftPMtime)") .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment Spacer() // Let's add a button on each row to reset all hours to false for the project. We do it directly in projects array Button { /*projects[projectNum]*/project.isHourSet = allHoursFalse } label: { Text("all off") } Spacer() // ADDED } } } struct HourButton: View,Identifiable { var id: String = "hour" @Binding 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) } } }
Topic: Programming Languages SubTopic: Swift Tags:
Feb ’24
Reply to Expressions are not allowed at the top level
An easy solution is to put this code in onAppear: struct ContentView: View { var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() .onAppear() { let range01INT1 = 100000 ... 199999 print("Range:",range01INT1,"Number of numbers", range01INT1.count) } } } And get: Range: 100000...199999 Number of numbers 100000 Note it's Xcode, not CodeX
Topic: Programming Languages SubTopic: Swift Tags:
Feb ’24
Reply to explanation of the following code step by step
You will have to restructure a lot. I let you try this. If that's what you want, try to understand code and ask for anything unclear. Please do it in a new thread, to avoid ever lasting thread. Note: that's quick and dirty code, just to have a starting point. There are clarification questions as comments in code. Please answer. extension String { 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 } } let allHoursFalse = [false, false, false, false, false, false] // To simplify code writing, we hard code: there are 6 hours defined struct TimeKeyinList: View { // }, Identifiable { @State var projects = [Project] () // Now need a State var to be able to modify // Not the correct var to keep hours for each project. @State var isSet: [Bool] = [true, false, false, true] @State private var hideTeco = false // What's the purpose of this ? Not used in code @State var filteredProjects: [Project] = [] /* { Init will be done in onAppear // removeTECO project projects.filter { project in !project.isTeco } }*/ var body: some View { VStack(spacing: 10) { ForEach($filteredProjects, id: \.id) { $project in TimeKeyInRow(id: project.shortname, projects: $projects, projectNum: project.projectRow) } } .labelsHidden() .onAppear() { // <<-- Added. All isHourSet initialised as false projects = [ Project(id: 1, projectRow: 0, name: "Project1, isTeco = false, shortname = MORN, leftPMTime = 131",shortname : "MORN", leftPMtime : 131, isTeco: false, isHourSet: allHoursFalse), Project(id: 2, projectRow: 1, name: "Project2, isTeco = false, shortname = MORN, leftPMTime = 122",shortname : "IFF", leftPMtime : 122, isTeco: true, isHourSet: allHoursFalse), Project(id: 3, projectRow: 2, name: "Project3, isTeco = false, shortname = MORN, leftPMTime = 133",shortname : "FFI", leftPMtime : 133, isTeco: true, isHourSet: allHoursFalse), Project(id: 4, projectRow: 3, name: "Project4, isTeco = false, shortname = MORN, leftPMTime = 444",shortname : "IFFCO", leftPMtime : 444, isTeco: false, isHourSet: allHoursFalse) ] filteredProjects = projects.filter { project in !project.isTeco // What does isTeco mean ???? } // for (row, project) in filteredProjects.enumerated() { // Replaced by array of Bool for hours in each project // isSet[row] = !project.isTeco // I do not understand what was isSet used for here. // } } } } struct Project: Identifiable { var id: Int = 0// UUID = UUID() var projectRow: Int // inside projects var name: String var shortname: String var leftPMtime: Int var isTeco: Bool var isHourSet: [Bool] // ### To store for each hour of the project // private enum CodingKeys : String, CodingKey { // case name, shortname, leftPMtime, isTeco // } } struct TimeKeyInRow: View,Identifiable { var id: String @Binding var projects: [Project] // we pass all projects as well as the projectNum to handle. That eases the update of the project properties var projectNum: Int // The project displayed in the row // @Binding var isSet: Bool ### No need, it is now a property in project let listOfPossibleHours: [Double] = [0.5, 1, 2, 3, 4, 8] var body: some View { HStack { Spacer() Text(projects[projectNum].shortname.paddedToWidth(5)) // Text(projects[1].shortname) .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment Spacer() ForEach(Array(listOfPossibleHours.enumerated()), id: \.offset) { (index, hour) in HourButton(id: "\(projects[projectNum].shortname)", isSet: $projects[projectNum].isHourSet[index], value: hour) // HourButton has changed, to pass value of isSet for each hour } Text("\(projects[projectNum].leftPMtime)") .font(.custom("Menlo", size: 16)) // <<-- To get proper alignment Spacer() // Let's add a button on each row to reset all hours to false for the project. We do it directly in projects array Button { projects[projectNum].isHourSet = allHoursFalse } label: { Text("all off") } Spacer() // ADDED } } } struct HourButton: View,Identifiable { var id: String = "hour" @Binding var isSet: Bool // A Binding, as we change the value and want it updated in TimeKeyInRow 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) } } }
Topic: Programming Languages SubTopic: Swift Tags:
Feb ’24
Reply to explanation of the following code step by step
That's a new question, it would be better to open a new thread. what if I want to let's say switch all the button back to false ? all buttons on a row (one project) ? On all rows ? IsSet was parameter by an array but this array is gone What do you mean ? Which array is gone ? Your spec is not clear. How do you want to switch them all to false ? With a button that would reset them all to false for a given project ? If so, add a button in TimeKeyInRow ; in the action, you will simple assign all isSet[I] to false. if something else, please explain But it seems you will have to change the design and have all the buttons status (isSet) for a given project, defined as a property: struct Project: Identifiable { var id: Int = 0// UUID = UUID() var name: String var shortname: String var leftPMtime: Int var isTeco: Bool var isHourSet : [Bool] In addition, what is set by isSet in TimeKeyinList ? What is isTeco. That's very confusing by now. @State var isSet: [Bool] = [true, false, false, true] In the present design, it applies to the whole project. What is it for a project ? If you want to manage isSet for each hour you should store it (as isHourSet) as a property of the project. To help, you need to very clearly explain what each of those variables refer to.
Topic: Programming Languages SubTopic: Swift Tags:
Feb ’24
Reply to explanation of the following code step by step
Let's decompose this code: ForEach(Array(filteredNonTECOProjects.enumerated()), id: \.offset) { (row, project) in TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row]) } ForEach(x) loops through all the elements of x In this case, they are the elements of the Array. The array is built from filteredNonTECOProjects filteredNonTECOProjects is enumerated(), which means the index of the array is retrieved and associated to the element of the array itself, to get a tuple: (row, project). id: \ .offset defines the id needed by ForEach which requires identifier (identifiable conformance). In the case, the offset of element in the array is used as identifier. So at the end, ForEach loops through the array and pass (row, project) to the closure that is executed at each step of ForEach. The closure { (row, project) in TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row]) } for each (row, project), we create a TimeKeyInRow view that will be displayed (either in VStack or List or whatever else). It passes the parameters required by TimeKeyInRow: id, project, isSet for the row. isSet[row] requires $, because TimeKeyInRow expects a Binding. Note on Binding. When you pass a parameter (isSet[row] to TimeKeyInRow) and this parameter will be modified in TimeKeyInRow, you need to retrieve the new value of isSet[row] in the caller, in the State var @State var isSet: [Bool] So, by using $, you pass a pointer to the var, and so the original var is modified when you change in TimeKeyInRow. It is the equivalent of inout parameter for plain Swift: Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead. You write an in-out parameter by placing the inout keyword right before a parameter’s type. An in-out parameter has a value that’s passed in to the function, is modified by the function, and is passed back out of the function to replace the original value. func swapTwoInts(_ a: inout Int, _ b: inout Int)  // You call with an & sign (instead of $ sign) swapTwoInts(&someInt, &anotherInt) Home that helps.
Topic: Programming Languages SubTopic: Swift Tags:
Feb ’24