TextField: Generic parameter 'Subject' could not be inferred - swift

I have a form view designed to edit data for an existing item and I'm able to display the Binded (bound?) value without a problem. The TextField gives me an error of "Generic parameter 'Subject' could not be inferred"
Here's the file where the problem is occurring.
import SwiftUI
struct EditMeasurementView: View {
#Binding var measurementItem: MeasurementItem
var body: some View {
Form {
Text("\(measurementItem.weight)")
TextField("Weight", text: $measurementItem.weight)
// TextField("Date", text: $measurementItem.mdate)
}
}
}
struct EditMeasurementView_Previews: PreviewProvider {
static var previews: some View {
EditMeasurementView(measurementItem: .constant(MeasurementItem(weight:"99",mdate:"1/1/2001")))
}
}
The first item in the form is fine and if I click on '$measurementItem' is is bound to the #Binding. The second item is where the error appears and if I click on '$measurementItem' there, it does not indicate is is linked back to the #Binding.
What am I missing?

Here is some code to duplicate the problem described above:
import SwiftUI
struct MeasurementItem {
let weight: String
let mdate: String
}
struct EditMeasurementView: View {
#State var measurementItem: MeasurementItem
var body: some View {
Form {
Text("\(measurementItem.weight)")
TextField("Weight", text: $measurementItem.weight)
}
}
}
struct EditMeasurementView_Previews: PreviewProvider {
static var previews: some View {
EditMeasurementView(measurementItem: MeasurementItem(weight:"99",mdate:"1/1/2001"))
}
}
The line TextField("Weight", text: $measurementItem.weight) will generate the error described by the OP.
However the real problem is not with TextField("Weight", text: $measurementItem.weight) itself but that $measurementItem.weight is a constant (i.e it is declared with let instead of var) and because it's a constant the TextField cannot change the value of $measurementItem.weight .

Related

Why do I keep getting a "Cannot find '...' in scope" error?

I am trying to make a second view on an app which is like a leaderboard. When the game has completed 10 rounds on ContentView, I want the calculated score to be stored and printed on the ScoreboardView (accessed by a button). I used #Binding to connect the score variable from one view to the other, but keep getting the "out of scope" error. Does anyone know why this is? Here is my code for the ScoreboardView:
'''
import SwiftUI
struct scoreboardView: View {
#Binding var score: Int
var body: some View {
List {
ForEach(1..<9) {
Text("Game \($0): \(score) ")
}
}
}
}
struct scoreboardView_Previews: PreviewProvider {
static var previews: some View {
scoreboardView(score: $scoreTracker)
}
}
'''
This is not my final code, therefore ignore the middle. However, I get the error in the last line of the initializing of the preview.
You don't have anything defined called scoreTracker in your code, but you're trying to use it in your preview. Instead, you can pass a constant in place of your binding (just for preview purposes):
struct scoreboardView: View {
#Binding var score: Int
var body: some View {
List {
ForEach(1..<9) {
Text("Game \($0): \(score) ")
}
}
}
}
struct scoreboardView_Previews: PreviewProvider {
static var previews: some View {
scoreboardView(score: .constant(50))
}
}

SwiftUI Preview not working with Binding<Bool> [duplicate]

I present this view as a sheet from its parent view
struct NamesView: View {
#Binding var match: Match
var body: some View {
...
}
}
Since the match source of truth is in the parent view presenting this NamesView sheet, when the view is constructed I pass in a $match binding and data flows as intended.
However, when constructing this view in a preview provider
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView()
}
}
the compiler says that NamesView() expects a match argument of type Binding<Match> (Match being the parent view presenting this view as a sheet). I'm not sure what would be a good way to proceed from here or if this is a limitation of SwiftUI.
If you want only constant preview, then it can be
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView(match: .constant(Match()))
}
}
if you want it in live, the it can be
struct NamesView_Previews: PreviewProvider {
struct BindingTestHolder: View {
#State private var testedMatch = Match()
var body: some View {
NamesView(match: $testedMatch)
}
}
static var previews: some View {
BindingTestHolder()
}
}
Try this:
struct NamesView_Previews: PreviewProvider {
static var previews: some View {
NamesView(match:.constant(Match()))
}
}
I wrote about this in depth here, but the short version is simply to add the following extension:
extension Binding {
public static func variable(_ value: Value) -> Binding<Value> {
var state = value
return Binding<Value> {
state
} set: {
state = $0
}
}
}
And then do...
struct NamesView_Previews : PreviewProvider {
static var previews: some View {
NamesView(match: .variable(Match()))
}
}
This lets you actually mutate the value, useful when doing live previews in Xcode 14.

Missing argument for parameter 'message' in call

I'm back with another question. I was following this guide: https://medium.com/#fs.dolphin/passing-data-between-views-in-swiftui-793817bba7b1
Everything worked from it, but the SecondView_Previews is throwing an error Missing argument for parameter 'message' in call. Here is my ContentView and SecondView
// ContentView
import SwiftUI
struct ContentView: View {
#State private var showSecondView = false
#State var message = "Hello from ContentView"
var body: some View {
VStack {
Button(action: {
self.showSecondView.toggle()
}){
Text("Go to Second View")
}.sheet(isPresented: $showSecondView){
SecondView(message: self.message)
}
Button(action: {
self.message = "hi"
}) {
Text("click me")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
struct SecondView: View {
#State var message: String
var body: some View {
Text("\(message)")
}
}
struct SecondView_Previews: PreviewProvider {
static var previews: some View {
SecondView() // Error here: Missing argument for parameter 'message' in call.
}
}
It tried changing it to SecondView(message: String) and the error changes to "Cannot convert value of type 'String.Type' to expected argument type 'String'"
Can someone please explain what I'm doing wrong, or how to correctly set up the preview. It all works fine when there's no preview. Thanks in advance!
struct ContentView: View {
#State var message: String //Define type here
var body: some View {
Text("\(message)")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(message: "Some text") //Passing value here
}
}

Why doesn't the Text update when using #Binding?

The Working works as expected.
But using the #Binding in the NotWorking example, doesn't seem to update the Text control. Why doesn't the #Binding version work, what am I missing here?
Initial Launch:
After Typing:
struct Working: View {
//Binding from #State updates both controls
#State private var text = "working"
var body: some View {
VStack {
TextField("syncs to label...", text: $text)
Text($text.wrappedValue)
}
}
}
struct NotWorking: View {
//Using the #Binding only updates the TextField
#Binding var text: String
var body: some View {
//This does not works
VStack {
TextField("won't sync to label...", text: $text)
Text($text.wrappedValue)
}
}
}
struct Working_Previews: PreviewProvider {
#State static var text = "not working"
static var previews: some View {
VStack {
Working()
NotWorking(text: $text)
}
}
}
Static #States don't work. It's the fact that it being static means that the struct Working_Previews isn't mutated when text is changed, so it won't refresh.
We can test this by changing from a PreviewProvider to an actual View:
struct ContentView: View {
#State static var text = "not working"
var body: some View {
VStack {
Working()
NotWorking(text: ContentView.$text)
}
}
}
This code gives the following runtime message:
Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
Thanks to #George_E. I define #State in a wrapper view and display that for the preview. The WrapperView simply displays the control that I want to preview but it contains the State.
struct Working_Previews: PreviewProvider {
//Define the State in a wrapper view
struct WrapperView: View {
#State var text = "Preview is now working!!!"
var body: some View {
NotWorking(text: $text)
}
}
static var previews: some View {
VStack {
Working()
//Don't display the view that needs the #Binding
//NotWorking(text: $text)
//Use the WrapperView that has #State and displays the view I want to preview.
WrapperView()
}
}
}

Mutable Binding in SwiftUI Live Preview

I have a ChildView with a variable:
#Binding var itemName: String
In this ChildView I have few buttons that change value of the variable:
Button(action: {
self.itemName = "different value"
})
I was trying to use Preview like this:
struct ChildView_Previews: PreviewProvider {
static var previews: some View {
ChildView(itemName: "test")
}
}
But I am getting an error:
Cannot convert value of type 'String' to expected argument type
'Binding'
I am aware that I can use Preview like below. And the error will be gone and preview will work, but... itemName will have constant value, it will not be mutable now, not interactive in Live Preview:
struct ChildView_Previews: PreviewProvider {
static var previews: some View {
ChildView(itemName: .constant("test"))
}
}
How to declare a binding in SwiftUI Preview to make it interactive?
Updates to a #State variable in a PreviewProvider appear to not update the the read-only computed property previews directly. The solution is to wrap the #State variable in a test holder view. Then use this test view inside the previews property so the Live Preview refreshes correctly. Tested and working in Xcode 11.2.1.
struct ChildView: View {
#Binding var itemName: String
var body: some View {
VStack {
Text("Name: \(itemName)")
Button(action: {
self.itemName = "different value"
}) {
Text("Change")
}
}
}
}
struct ChildView_Previews: PreviewProvider {
struct BindingTestHolder: View {
#State var testItem: String = "Initial"
var body: some View {
ChildView(itemName: $testItem)
}
}
static var previews: some View {
BindingTestHolder()
}
}
If you need a value that can be changed in the live preview, I like to use this helper class:
struct BindingProvider<StateT, Content: View>: View {
#State private var state: StateT
private var content: (_ binding: Binding<StateT>) -> Content
init(_ initialState: StateT, #ViewBuilder content: #escaping (_ binding: Binding<StateT>) -> Content) {
self.content = content
self._state = State(initialValue: initialState)
}
var body: some View {
self.content($state)
}
}
Use it like so:
struct YourView_Previews: PreviewProvider {
static var previews: some View {
var yourVar = "example"
BindingProvider(yourVar) { binding in
YourView(initVar: binding)
}
}
}
This allows you to test changing the binding in the live preview.