I'm working on my first Swift/SwiftUI project (other than the tutorials) and have run into what I think is a common problem -- the error Generic parameter 'Parent' could not be inferred -- toward the top of the view when the problem is actually with a List I'm trying to generate lower down in the form.
The app I'm building is a simple invoicing app: the user fills out the form fields and sends the invoice. An invoice can have multiple line items that the user enters one at a time and then are appended to a dictionary that should display inside the form.
This is the relevant variables from the top of the struct and the beginning of the view, where I hope I'm declaring the variables for line items correctly to modify them based on user input.
*Edited following #asperi's advice below.
#State private var lineItems = [LineItem]()
#State private var lineItem = LineItem()
struct LineItem: Codable, Hashable {
var productSku: String = ""
var productName: String = ""
var quantity: Double = 0
var unitPrice: Double = 0
}
func addLineItem(lineItem: LineItem){
lineItems.append(lineItem)
}
...
var body: some View {
NavigationView {
Form {
Section(header: Text("Customer Information")) { <-- error appears here
TextField("Customer Name", text: $customerName)
TextField("Customer Email", text: $customerEmail)
}
Here's the relevant portion of the form, where I'm trying to list all current line items and then allow the user to insert additional line items. I don't get any error until I add the List code, so I'm pretty sure I'm doing something wrong there.
Section(header: Text("Items")) {
List(lineItems, id: \.self) { item in
LineItemRow(lineItem: item)
Text(item.productName)
}
TextField("Product SKU", text: $productSKU)
TextField("Poduct Name", text: $productName)
TextField("Unit Price", text: $unitPrice, formatter: DoubleFormatter())
Picker("Quantity", selection: $quantity) {
ForEach(0 ..< 10) {
Text("\($0)")
}
}
Button(action: {
self.addLineItem(lineItem: LineItem(productSku:$productSKU,
productName:$productName,
quantity:$unitPrice,
unitPrice:$quantity))
print($lineItems)
}, label: {
Text("Add line item")
})
}
I've tested the button functionality in the console and it does seem to be appending to the dictionary correctly, but I need it to display as well.
I'm probably getting something very basic wrong with the List. Any advice?
For reference, here's the whole view:
struct AddInvoiceForm: View {
#State private var invoiceNumber: String = ""
#State private var _description: String = ""
#State private var dueDate: Date = Date()
#State private var sendImmediately: Bool = true
#State private var currency = 0
#State private var paymentType = 0
#State private var customerName: String = ""
#State private var customerEmail: String = ""
#State private var productSKU: String = ""
#State private var productName: String = ""
#State private var quantity: Int = 0
#State private var unitPrice: String = ""
#State private var lineItems = [LineItem]()
#State private var lineItem = LineItem()
struct LineItem: Codable, Hashable {
var productSku: String = ""
var productName: String = ""
var quantity: Double = 0
var unitPrice: Double = 0
}
func addLineItem(lineItem: LineItem){
lineItems.append(lineItem)
}
#State private var totalAmount: Double = 0.0
static let currencies = ["USD","GBP"]
var body: some View {
NavigationView {
Form {
Section(header: Text("Customer Information")) {
TextField("Customer Name", text: $customerName)
TextField("Customer Email", text: $customerEmail)
}
Section(header: Text("Invoice Information")) {
TextField("Invoice Number", text:$invoiceNumber)
TextField("Description", text:$_description)
DatePicker(selection: $dueDate, in: Date()..., displayedComponents: .date) {
Text("Due date")
}
Picker("Currency", selection: $currency) {
ForEach(0 ..< Self.currencies.count) {
Text(Self.currencies[$0])
}
}
}
Section(header: Text("Items")) {
List(lineItems, id: \.self) { item in
LineItemRow(lineItem: item)
Text(item.productName)
}
TextField("Product SKU", text: $productSKU)
TextField("Poduct Name", text: $productName)
TextField("Unit Price", text: $unitPrice, formatter: DoubleFormatter())
Picker("Quantity", selection: $quantity) {
ForEach(0 ..< 10) {
Text("\($0)")
}
}
Button(action: {
self.addLineItem(lineItem: LineItem(productSku:$productSKU,
productName:$productName,
quantity:$unitPrice,
unitPrice:$quantity))
print($lineItems)
}, label: {
Text("Add line item")
})
}
Section(header: Text("Totals")) {
Text("\(totalAmount)")
}
Section {
Button(action: {
print(Text("Send"))
}, label: {
Text("Send invoice")
})
}
}.navigationBarTitle("Invoice Details")
}
}
}
List(lineItems, id: \.productSku) { item in <-- I get the error when I add this
Your item is dictionary, but dictionary does not have .productSku key path, so the error.
I assume most simple & correct would be to make Item as struct
struct LineItem {
var productSku: String
...
}
...
#State private var lineItems = [LineItem]()
#State private var lineItem = LineItem()
...
List(lineItems, id: \.productSku) { item in
Related
I have a nestled foreach loop and I want to modify data for each element in the nestled loop. Like this:
struct trainingView: View {
#ObservedObject var workout: Workout
#State private var reps: Int = 0
var body: some View {
ForEach(workout.exercises, id: \.self) { exercise in
Text(exercise.nameOfExercise)
ForEach(0..<exercise.amountOfSets, id: \.self) { Set in
Text("\(Set+1) Set")
Text(reps)
Stepper("Reps", value: $reps , in: 0...30)
}
}
}
}
I would like the variable rep to be independent from changes in an another elements stepper. If you need better examples of the code just ask.
This does not work as one change affects all elements. I have also tried making an #State object in the nestled loop but this doesn't work either. I have also tried making an array to store the data but this seemed complicated since the size of the array can change from different times. I have tried to search for the information on here but haven't found anyone answering it. I am fairly new to swiftUI and in need of help.
The Stepper in your example takes a Binding to a numeric value. If you create an array-of-arrays, you can create a Binding by accessing each row and element within it via their indices, e.g.:
struct Exercise: Identifiable, Equatable, Hashable {
let id = UUID()
let name: String
var sets: [Set]
}
struct Set: Identifiable, Equatable, Hashable {
let id = UUID()
let name: String
let requiredReps: Int = 10
var reps: Int = 0
}
class Workout: ObservableObject {
#Published var exercises: [Exercise]
init(exercises: [Exercise]) {
self.exercises = exercises
}
}
struct ContentView: View {
#StateObject var workout = Workout(exercises: [
Exercise(name: "Squats", sets: [Set(name: "Set 1"), Set(name: "Set 2"), Set(name: "Set 3")]),
Exercise(name: "Lunges", sets: [Set(name: "Set 1"), Set(name: "Set 2"), Set(name: "Set 3")]),
Exercise(name: "Burpees", sets: [Set(name: "Set 1"), Set(name: "Set 2"), Set(name: "Set 3")])
])
var body: some View {
List {
ForEach(workout.exercises) { exercise in
Section(exercise.name) {
let rowIndex = workout.exercises.firstIndex(of: exercise)!
HStack {
ForEach(exercise.sets) { set in
let objectIndex = exercise.sets.firstIndex(of: set)!
VStack {
Text(set.name + ": ") + Text(set.reps, format: .number) + Text("/") + Text(set.requiredReps, format: .number)
Stepper("Reps", value: $workout.exercises[rowIndex].sets[objectIndex].reps, in: 0...set.requiredReps)
.labelsHidden()
}
}
}
}
}
.padding()
}
}
}
Im trying to get sort id which is coming from my server . I can get UUID for each sort type in menu picker but how can I get their id instead of UUID when I select a sort type in Menu.As you can see in my code I can get UUID with Just(selection) but I need to get sort id which is data.id .
#State private var selection = UUID()
#Binding var sortClicked : Bool
#ObservedObject var productListViewModel = ProductListViewModel()
var sortListArray : [ProductListSortAndFilterList]
var body : some View {
HStack(alignment: .center){
Spacer()
Picker(selection: $selection, label: SortView().frame(height:UIScreen.main.bounds.height * 0.050), content: {
ForEach(sortListArray, id: \.uid, content: { item in
if item.id == "sort" {
ForEach(item.sortList ?? [],id:\.uid) { data in
Text(data.name ?? "")
.tag(data.id)
}
.onReceive(Just(selection)) { (value) in
print(value)
}
}
})
})
.pickerStyle(MenuPickerStyle())
.padding()
.background(Color(.systemBackground).edgesIgnoringSafeArea(.all))
.cornerRadius(5)
I am trying to write code containing an If statement which depends on the responses entered by user for a series of questions. Different responses will add different values to variable "Score". My code is fairly simple, but I cannot figure why the error message "Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols" keeps popping up around my If statement. The If statement is at the very end. Thank you to anyone who is willing to help!
import SwiftUI
struct InfluenceLevel: View {
#State private var name = ""
#State private var age = "0"
#State private var gender = ""
#State private var nationality = ""
#State private var Instagram = ""
#State private var Celebrities = ""
#State private var Time = ""
#State private var products = ""
#State private var score = 0
var body: some View {
VStack {
Text("Please fill out the following questions to the best of your ability, allowing us to assess your susceptibility to advertising on social media")
Form{
Group{
Text("What is your gender? \(gender)" )
TextField("(Male/Female/Other):", text: $gender)
Text("Are you Chinese? \(nationality)" )
TextField("(Yes/No):", text: $nationality)
Text("Do you use Instagram? \(Instagram)" )
TextField("(Yes/No):", text: $Instagram)
}
Group{
Text("Do you mainly follow celebrities on social media? \(Celebrities)" )
TextField("(Yes/No):", text: $Celebrities)
Text("Do you spend less than an hour on social media every day? \(Time)" )
TextField("(Yes/No)", text: $Time)
Text("Do you use more than half of your monthly expenditure on stuff seen online? \(products)" )
TextField("(Yes/No)", text: $products)
}
}
}
.navigationBarTitle("Level of Influence")
if gender == "Male" {
score = score + 1
}
}
}
struct InfluenceLevel_Previews: PreviewProvider {
static var previews: some View {
InfluenceLevel()
}
}
Try this:
enum GenderEnum: Int {
case female, male, other
init(str: String) {
switch str {
case "Male":
self = .male
case "Female":
self = .female
default:
self = .other
}
}
}
struct InfluenceLevel: View {
#State private var gender = ""
#State private var score = 0
var body: some View {
let binding = Binding<String>(get: {
self.gender
}, set: {
self.gender = $0
score = GenderEnum(str: $0).rawValue
print(score)
})
VStack {
Text("Please fill out the following questions to the best of your ability, allowing us to assess your susceptibility to advertising on social media")
Form{
Group{
Text("What is your gender? \(gender)" )
TextField("(Male/Female/Other):", text: binding)
}
}
.navigationBarTitle("Level of Influence")
}
}
struct Gg: Identifiable{
let id: Int
let task: String
}
struct ContentView: View {
#State private var items = [Gg(id: 1, task:"take the trash out"), Gg(id: 2, task:"Go for a run")]
var body: some View {
AddTaskUIView(title: "Add Item", isShown: $isPresented, text: $text, onDone: { text in
self.items.append(text)
self.text = ""
})
}}
I am getting an error
Cannot convert value of type 'String' to expected argument type 'Gg'
how to I append to the task of the structure Gg?
I am new to swift so I would appriacte any help thanks.
struct Gg: Identifiable{
let id: UUID = UUID()
let task: String
init(_ task: String) {
self.task = task
}
}
...
#State private var items = [Gg("take the trash out"), Gg("Go for a run")]
...
self.items.append(Gg(text))
Heres a solution that does not have the problem of keeping track of the id yourself.
Hello everyone
I am creating a form that allows me to modify the data of #EnvironmentObject variable.
Therefore, I would like to be able to create a Picker that returns a String. However, after many unsuccessful attempts, I have the impression that a Picker cannot return a String.
Anyone would have an idea for a Picker that returns a String (maybe through UIKit ?).
Here's my code :
struct UserStruct {
var firstName: String
var lastName: String
var birthDate: Int
var city: String
var postalCode: Int
var street: String
var streetCode: String
var country: String
}
class User: ObservableObject {
#Published var userProfile = UserStruct()
// Other stuff here
}
// Then in my FormView:
// I declare the object as #EnvironmentObject
#EnvironmentObject var userStore: User
// I declare an array which contains all the country for the picker
let country = ["France", "Russie", "USA"]
// In my var body: some View...
// Trying to change the value of country of the userStore object
Picker(selection: $userStore.userProfile.country, label: Text("Make a choice")) {
ForEach(0 ..< country.count) { index in
Text(self.country[index]).tag(index)
}
Thank you all for your help.
you could try something like this:
let country = ["France", "Russie", "USA"]
#State var countrySelection = 0
Picker(selection: Binding(get: {
self.countrySelection
}, set: { newVal in
self.countrySelection = newVal
self.userStore.userProfile.country = self.country[self.countrySelection]
}), label: Text("Make a choice")) {
ForEach(0 ..< country.count) { index in
Text(self.country[index]).tag(index)
}
}