I'm having a problem trying to get textfields working in SwiftUI.
I get Fatal error: Accessing State> outside View.body whenever I try to run the following code.
Anyone have a suggestion?
struct SearchRoot : View {
#State var text: String = ""
var body: some View {
HStack {
TextField($text,
placeholder: Text("type something here..."))
Button(action: {
// Closure will be called once user taps your button
print(self.$text)
}) {
Text("SEND")
}
}
}
}
I'm running Xcode Version 11.0 beta (11M336w) on macOS 10.15 Beta (19A471t)
Edit: Simplified code, still getting the same error.
struct SearchRoot : View {
#State var text: String = ""
var body: some View {
TextField($text,
placeholder: Text("type something here..."))
}
}
The compiler emits an error if the $ operator is used outside body, in a View.
The button initializer is defined as:
init(action: #escaping () -> Void, #ViewBuilder label: () -> Label)
You're using $ in an escaping closure, in the first snippet of code.
That means the action may outlive (escape) the body, hence the error.
The second snippet compiles and works fine for me.
Eureka! SwiftUI wants a single source of truth.
What I neglected to include in my original code snippets is that this struct is within a tabbed application.
To fix this I needed to define the #State var text: String = "" in the struct that creates the top level TabbedView, then use $Binding in the SearchRoot.
I'm not sure if this is works as designed or just a beta 1 issue, but it's the way it works for now.
struct ContentView : View {
#State private var selection = 0
#State private var text: String = "searching ex"
var body: some View {
TabbedView(selection: $selection){
ShoppingListRoot().body.tabItemLabel(Text("Cart")).tag(0)
SearchRoot(text: $text).body.tabItemLabel(Text("Search")).tag(1)
StoreRoot().body.tabItemLabel(Text("Store")).tag(2)
BudgetRoot().body
.tabItemLabel(Text("Budget"))
.tag(3)
SettingsRoot().body
.tabItemLabel(Text("Settings"))
.tag(4)
}
}
}
Related
Hello I am running into a problem here and I do not have a consistent behavior between my .sheet() view when running on ios13 or ios14
I got a view like this :
#State private var label: String = ""
#State private var sheetDisplayed = false
///Some code
var body: some View {
VStack {
Button(action: {
self.label = "A label"
self.isDisplayed = true
}) {
Text("test")
}
}.sheet(isPresented: $sheetDisplayed, onDismiss: {
self.label = ""
}) {
Text(self.label)
}
}
On ios 13 this work as expected btn click -> set label -> call sheet -> display "A label" in a Text view.
On ios14 I got an empty string in self.label when in sheet closure, hence it does not display anything.
Did I missed something ? Is it an iOS 14 bug or did I had it wrong on ios13 and that got corrected.
PS: I have a couple of other variables that are passed in the closure I simplified it.
Your code have expectation of view update/creation order, but in general it is undefined (and probably changed in iOS 14).
There is explicit way to pass information inside sheet - use different sheet creator, ie. .sheet(item:...
Here is working reliable example. Tested with Xcode 12 / iOS 14
struct ContentView: View {
#State private var item: Item?
struct Item: Identifiable {
let id = UUID()
var label: String = ""
}
var body: some View {
VStack {
Button(action: {
self.item = Item(label: "A label")
}) {
Text("test")
}
}.sheet(item: $item, onDismiss: {
self.item = nil
}) {
Text($0.label)
}
}
}
This is some really strange behaviour in iOS 14, which doesn't appear to be documented.
Using the other answer here and the comment on this thread, I used #Binding to solve the issue as it seemed the cleanest and most SwiftUI-esq solution.
I have no idea why this behaviour has changed, and it seems less intuitive than before, so I'm assuming its a bug!
An example:
struct MainView: View {
#State private var message = ""
#State private var showSheet = false
var body: some View {
Button(action: {
self.message = "This will display the correct message"
self.showSheet = true
}, label: {
Text("Test Button")
})
.sheet(isPresented: self.$showSheet) {
SheetView(message: self.$message)
}
}
}
struct SheetView: View {
#Binding var message: Int
var body: some View {
Text(self.message)
}
}
The behaviour changed with SwiftUI 2.0, so it affects MacOS 11 as well, just adding a binding to the view fixes it even when that binding is never used, which makes me think this is an implementation bug.
Additionally just using the details state variable in a Text() within the body of the view also fixes it.
struct MyViewController : View {
#State var details: String?
#State var showDetails = false
// #Binding var havingAbindingFixesIt: String?
var body: some View {
VStack {
// Text(details ?? "")
Text("Tap here for details")
.onTapGesture {
self.details = "These are the details"
self.showDetails.toggle()
}
.sheet(isPresented: $showDetails) { Text(details ?? "") }
}
}
}
So I'm doing some refactoring and I ran across this line of code that I wanted to refactor:
struct MyView: View {
#State private var myArrayOfCustomObjects = [CustomObject]
let text: String
var body: some View {
Text(text)
}
}
Then when I wanted to refactor the view as so..
struct ExtractedView: View {
#Binding var customObjects: [CustomObject]
let text: String
init(customObjects: Binding<Array<CustomObject>>, text: String) {
self.customObjects = customObjects // Error: 'self' used before all stored properties are initialized
// Also tried _customObjects = customObjects
self.text = text
}
var body: some View {
Text(text)
}
}
This code is simplified of course but I fear I may be getting that error due to some complexity I'm not exposing in the example. Any feedback is welcome
What am I doing wrong??
( I also have an Environment instance (managedObjectContext) and a coreData class - which has some logic inside of the init that are being initialized too but didn't think it was relevant for this code example )
This will work! also try clean your build folder and build your project first.
struct ExtractedView: View {
#Binding var customObjects: [CustomObject]
let text: String
init(customObjects: Binding<Array<CustomObject>>, text: String) {
self._customObjects = customObjects
self.text = text
}
var body: some View {
Text(text)
}
}
struct CustomObject { }
SwiftUI Pickers appear not to work in iOS 14 as they used to.
The lower one works perfectly, while the first one produces this error:
"displayModeButtonItem is internally managed and not exposed for DoubleColumn style. Returning an empty, disconnected UIBarButtonItem to fulfill the non-null contract."
Is there anything I am missing? I don't know how to get the upper version to work again?
It does work if I change the picker style to the Wheel, however I prefer to use the "DefaultPickerStyle", where it works like a Navigationlink revealing the list of options in a second page.
struct WeekdayPickerViewNEW1 : View{
#Binding var selection: Int
var text: String
var body: some View {
Picker(selection: self.$selection, label: Text(self.text)){
Text("1").tag(1)
Text("2").tag(2)
Text("3").tag(3)
}
}
}
struct WeekdayPickerViewNEW2 : View{
#State var test: Int = 0
var text: String
var body: some View {
Picker(selection: self.$test, label: Text(self.text)){
Text("A").tag(1)
Text("B").tag(2)
Text("C").tag(3)
}
}
}
Hello I am running into a problem here and I do not have a consistent behavior between my .sheet() view when running on ios13 or ios14
I got a view like this :
#State private var label: String = ""
#State private var sheetDisplayed = false
///Some code
var body: some View {
VStack {
Button(action: {
self.label = "A label"
self.isDisplayed = true
}) {
Text("test")
}
}.sheet(isPresented: $sheetDisplayed, onDismiss: {
self.label = ""
}) {
Text(self.label)
}
}
On ios 13 this work as expected btn click -> set label -> call sheet -> display "A label" in a Text view.
On ios14 I got an empty string in self.label when in sheet closure, hence it does not display anything.
Did I missed something ? Is it an iOS 14 bug or did I had it wrong on ios13 and that got corrected.
PS: I have a couple of other variables that are passed in the closure I simplified it.
Your code have expectation of view update/creation order, but in general it is undefined (and probably changed in iOS 14).
There is explicit way to pass information inside sheet - use different sheet creator, ie. .sheet(item:...
Here is working reliable example. Tested with Xcode 12 / iOS 14
struct ContentView: View {
#State private var item: Item?
struct Item: Identifiable {
let id = UUID()
var label: String = ""
}
var body: some View {
VStack {
Button(action: {
self.item = Item(label: "A label")
}) {
Text("test")
}
}.sheet(item: $item, onDismiss: {
self.item = nil
}) {
Text($0.label)
}
}
}
This is some really strange behaviour in iOS 14, which doesn't appear to be documented.
Using the other answer here and the comment on this thread, I used #Binding to solve the issue as it seemed the cleanest and most SwiftUI-esq solution.
I have no idea why this behaviour has changed, and it seems less intuitive than before, so I'm assuming its a bug!
An example:
struct MainView: View {
#State private var message = ""
#State private var showSheet = false
var body: some View {
Button(action: {
self.message = "This will display the correct message"
self.showSheet = true
}, label: {
Text("Test Button")
})
.sheet(isPresented: self.$showSheet) {
SheetView(message: self.$message)
}
}
}
struct SheetView: View {
#Binding var message: Int
var body: some View {
Text(self.message)
}
}
The behaviour changed with SwiftUI 2.0, so it affects MacOS 11 as well, just adding a binding to the view fixes it even when that binding is never used, which makes me think this is an implementation bug.
Additionally just using the details state variable in a Text() within the body of the view also fixes it.
struct MyViewController : View {
#State var details: String?
#State var showDetails = false
// #Binding var havingAbindingFixesIt: String?
var body: some View {
VStack {
// Text(details ?? "")
Text("Tap here for details")
.onTapGesture {
self.details = "These are the details"
self.showDetails.toggle()
}
.sheet(isPresented: $showDetails) { Text(details ?? "") }
}
}
}
Does anyone know why I get errors when using a variable for my navigationBarTitle ?
The error changes depending on where I put this line of code, or what the main container is. Currently I get the usual 'CGFloat' is not convertible to 'CGFloat?'
But if I swap the navigationBarTitle with the padding then I get Ambiguous reference to member 'navigationBarTitle(_:displayMode:)'
If I swap the sectionName for "" then the compiler is happy.
struct TestView : View {
private var sectionName = "Hello"
var body : some View {
VStack(alignment: .leading) {
Text("Hello")
}
.navigationBarTitle(sectionName, displayMode:.inline)
.padding(.horizontal, 12.0)
}
}
First, to see the real error you may update Xcode to 11.4 version - it shows errors better.
The second - if you will go to SwiftUI framework, you will see:
public func navigationBarTitle(_ titleKey: LocalizedStringKey, displayMode: NavigationBarItem.TitleDisplayMode) -> some View
so, the error is that you use String instead of LocalizedStringKey
the solution is:
// replace this
//private var sectionName = "Hello"
// to this:
private var sectionName: LocalizedStringKey = "Hello"