Home > front end >  Missing parameter but need parameter to be changed based on user input
Missing parameter but need parameter to be changed based on user input

Time:10-10

This file is for the main structure of the application. This is where the error is coming from which is "Missing argument for parameter 'numberOfDoors' in call". This is because it wants me to add

ContentView(numberOfDoors: <#Int#>) 

but im having trouble finding out how I can get what the user chooses to be the int instead of me putting a number in there statically.

import SwiftUI

@main
struct NumOfDoorsApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
 }

This is my project file.

import SwiftUI

struct ContentView: View {

@State var numberOfDoors: Int
@State var multiOptions: Array<String>

init(numberOfDoors: Int) {
    self.numberOfDoors = numberOfDoors
    self.multiOptions = [String](repeating: "", count: numberOfDoors)
}

var body: some View {
    NavigationView {
        Form{
            Section {
                Picker("Number of doors", selection: $numberOfDoors) {
                    ForEach(1 ..< 64) {
                        Text("\($0) doors")
                    }
                }
                
                ForEach(multiOptions.indices, id: \.self) { index in
                    TextField("Enter your option...", text: $multiOptions[index])
                        .padding()
                        .textFieldStyle(RoundedBorderTextFieldStyle())
                }
            }
            
            Section {
                Text("\(numberOfDoors   1)")
            }
        }
    }
}
}

CodePudding user response:

You write an initializer with a parameter, namely number of doors. Your App now wants to call ContentView and therefore the View needs to be initialized. Since you defined the initializer to be called with a parameter, your App needs to call ContentView with a parameter numberOfDoors.

Looking at your code, I see that the user is allowed to pick the number of doors in the app. Therefore you should ask you whether contentView needs to be initialized with a parameter or you want to start ContentView with numberOfDoors with a initial value.

So, my question back, why do you need to initialize ContentView? And, can you not start ContentView with:

@State private var numberOfDoors = 1 ?

Kind regards, McUserT

CodePudding user response:

One of the most important parts of SwiftUI programming is creating an appropriate model for your views.

@State properties are OK for local, often independent properties, but they typically aren't a good solution for your main data model or where a model needs to be manipulated by the user.

In your case you want the size of the array to change based on the selected number of doors, so you need somewhere for that procedural code to live; The model is that place.

Here is a simple model object you can use

class DoorModel: ObservableObject {
    
    @Published var numberOfDoors: Int {
        didSet {
            self.adjustArray(newSize: numberOfDoors)
        }
    }
    @Published var doors:[String]
    
    init(numberOfDoors: Int) {
        self.numberOfDoors = numberOfDoors
        self.doors = [String](repeating: "", count: numberOfDoors)
    }
    
    private func adjustArray(newSize: Int) {
        let delta = newSize - doors.count
        
        print("new size = \(newSize) Delta = \(delta)")
        
        if delta > 0 {
            doors.append(contentsOf:[String](repeating: "", count: delta))
        } else if delta < 0 {
            doors.removeLast(-delta)
        }
    }
}

Note that you still need to supply a starting number of doors via the initialiser for your model. Whenever that value changes, the didSet property observer calls a function to add or remove elements from the end of the array.

You can use this model in your view with an @StateObject decorator. This ensures that a single instance is created and reused as your view is redrawn

struct ContentView: View {
    
    @StateObject var model = DoorModel(numberOfDoors: 1)
    
    var body: some View {
        NavigationView {
            Form{
                Section {
                    Picker("Number of doors", selection: $model.numberOfDoors) {
                        ForEach(1 ..< 64) { index in
                            Text("\(index) doors").tag(index)
                        }
                    }
                    
                    ForEach($model.doors.indices, id: \.self) { index in
                        TextField("Enter your option...", text: $model.doors[index])
                            .padding()
                            .textFieldStyle(RoundedBorderTextFieldStyle())
                    }
                }
            }
        }
    }
}

I added the .tag modifier to ensure that the picker works correctly with your 1-based list; By default the tag will be 0 based.

  • Related