iOS14 introducing errors with @State bindings
It looks like in iOS 14 the sheet(isPresented:content:)
is now created beforehand, so any changes made to selectedModel
are ignored.
Try using sheet(item:content:)
instead:
var body: some View {
List {
...
}
.sheet(item: self.$selectedModel) {
SpeakerDetailView(speaker: $0)
}
}
and dismiss the sheet using @Environment(\.presentationMode)
:
struct SpeakerDetailView: View {
@Environment(\.presentationMode) private var presentationMode
var speaker: Speaker
var body: some View {
Text("Speaker view")
.onTapGesture {
presentationMode.wrappedValue.dismiss()
}
}
}
Cant get my sheet to display the right information
In iOS 14 sheet(isPresented:content:)
is now created beforehand and the view is not refreshed when the $businessSheetPresented
changes.
Instead of .sheet(isPresented:content:)
.sheet(isPresented: $businessSheetPresented) {
if let pressedUser = pressedUser {
DisplayBusinessSheet(user: pressedUser)
}
}
you should use .sheet(item:content:)
.sheet(item: $pressedUser) { user in
DisplayBusinessSheet(user: pressedUser)
}
Note: with the above solution you don't really need businessSheetPresented
unless it's used in some other place apart from triggering the sheet.
Displaying a Sheet from multiple options in swiftUI
Here is another approach for your problem which uses sheet(item:content:)
struct ContentView: View {
@State private var selectedSpeaker: Speaker?
@State private var selectedMicrophone: Microphone?
@State private var selectedAmp: Amplifier?
@State private var showSettingsSheet = false
var body: some View {
List {
settingsSection
microphonesSection
// more sections
}
}
var settingsSection: some View {
Button(action: {
self.showSettingsSheet = true
}) {
Text("Settings")
}
.sheet(isPresented: self.$showSettingsSheet) {
SettingsView()
}
}
@ViewBuilder
var microphonesSection: some View {
Button(action: {
self.selectedMicrophone = microphones[0]
}) {
Text("Mic 1")
}
Button(action: {
self.selectedMicrophone = microphones[1]
}) {
Text("Mic 2")
}
.sheet(item: self.$selectedMicrophone) {
MicDetailView(microphone: $0)
}
}
}
This way you also don't need enum ActiveSheet
.
You can always use @Environment(\.presentationMode)
to dismiss a sheet, no need to pass the variable to the sheet (as in SettingsView(showSheet: self.$showingSheet)
):
struct SettingsView: View {
@Environment(\.presentationMode) private var presentationMode
var body: some View {
Text("SettingsView")
.onTapGesture {
presentationMode.wrappedValue.dismiss()
}
}
}
@State property value is not retain when called within the sheet(item:) method
I think you're getting caught by the fact that sheet(item:)
only re-renders based on item
-- not the encapsulating View's @State
variables. As long as you explicitly pass isReseting
through the item
, it will work:
struct Item: Identifiable, Equatable {
var id = UUID()
var name:String
var active:Bool
}
struct ContentView: View {
var items:[Item] = [Item(name: "Oranges", active: true),
Item(name: "Apples", active: false),
Item(name: "Cookies", active: false) ]
struct SheetItem : Identifiable {
var item: Item
var isReseting: Bool
var id: UUID {
self.item.id
}
}
@State private var selectedItem: SheetItem?
@State private var isReseting: Bool?
var body: some View {
List{
ForEach(items){ item in
HStack{
Text(item.name)
Button(item.active ? "Reset": "Initiate"){
isReseting = true
selectedItem = SheetItem(item: item, isReseting: isReseting ?? false)
}
.padding()
.background(item.active ? Color.blue: Color.gray)
.foregroundColor(.white)
.cornerRadius(30)
.frame(width: 100, height: 65)
}
}
}
.sheet(item: $selectedItem) { item in
let _ = print("Value in sheet: \(item.isReseting)")
if item.isReseting == true {
Text("It's reseting!!!")
} else {
Text("It's NOT reseting")
}
}
}
}
A second way to accomplish this is explicitly pass a Binding
(even though you don't need to mutate it in the sheet), since it will maintain its link to the @State
variable:
struct Item: Identifiable{
var id = UUID()
var name:String
var active:Bool
}
struct ContentView: View {
var items:[Item] = [Item(name: "Oranges", active: true),
Item(name: "Apples", active: false),
Item(name: "Cookies", active: false) ]
@State private var selectedItem: Item?
@State private var isReseting: Bool?
var body: some View {
List{
ForEach(items){ item in
HStack{
Text(item.name)
Button(item.active ? "Reset": "Initiate"){
selectedItem = item
isReseting = true
let _ = print("Value after button tap: \(isReseting)")// output: Value after button tap: Optional(true)
}
.padding()
.background(item.active ? Color.blue: Color.gray)
.foregroundColor(.white)
.cornerRadius(30)
.frame(width: 100, height: 65)
}
}
}
.sheet(item: $selectedItem){ item in
// I'm expecting isReseting to be true here, but it's nil
SheetView(isReseting: $isReseting)
}
}
}
struct SheetView : View {
@Binding var isReseting : Bool?
var body: some View {
let _ = print("Value in sheet: \(isReseting)")// outputs: Value in sheet: nil
if isReseting == true {
Text("It's reseting!!!")
}else{
Text("It's NOT reseting")
}
}
}
Related Topics
Autolayout: Removefromsuperview/Removeconstraints Throws Exception and Crashes Hard
Programmatically Navigate to Another View Controller/Scene
Uisearchbar Increases Navigation Bar Height in iOS 11
Xcode 9 Swift Language Version (Swift_Version)
Uigesturerecognizer and Uitableviewcell Issue
Customizing the More Menu on a Tab Bar
How to Add Background Image on iPhone Navigation Bar
Upload Image with Multipart Form-Data iOS in Swift
How to Implement Pageview in Swiftui
Check If a Uialertview Is Showing
Google Sign-In Crashes on iOS 9 Attempting to Call Canopenurl
Allow Only Alphanumeric Characters for a Uitextfield
Dismiss Keyboard on Touch Anywhere Outside Uitextfield