I want to change the result of the bool Enabled, but don't know how to do it.
I need the bool to enable/disable a function in another UIViewRepresentable.
Code:
import SwiftUI
var Enable = true
struct ContentView: View {
#State private var Route = false
var body: some View {
VStack{
Button(action: {
self.Route.toggle()
}) {
Text("Route >")
}
if Route {
Enable = false
}
}
}
}
If I use this code it prints the error:
Argument type '()' does not conform to expected type 'View'
As mentioned in comments, by convention, variables should begin with lowercase and types (classes, enum, struct) with uppercase.
Also, your code should go inside the closure.
var enable = true
struct ContentView: View {
#State private var route = false
var body: some View {
VStack{
Button(action: {
self.route.toggle()
if self.route { enable = false }
}) {
Text("Route >")
}
}
}
}
Related
I am failing miserably trying to wrap my head around how to use .sheet(item:content:). I know that there is a lot of information here and on other platforms, but I just cannot get it to work...
Here's an abstraction of the view I'd like to have – and I don't know what I am doing wrong here:
import SwiftUI
enum SheetView: String, Identifiable {
var id: Self { self }
case sheetA = "Sheet A"
case sheetB = "Sheet B"
}
struct SheetViewTest: View {
#State private var showSheet: SheetView? = nil
var body: some View {
Form {
Button(action: {
showSheet = .sheetA
print("Button \(showSheet?.rawValue) tapped…")
}, label: {
Text(SheetView.sheetA.rawValue)
})
Button(action: {
showSheet = .sheetB
print("Button \(showSheet?.rawValue) tapped…")
}, label: {
Text(SheetView.sheetB.rawValue)
})
}
.sheet(item: $showSheet) { sheet -> View in
Text(sheet.rawValue)
}
}
}
struct SheetViewTest_Previews: PreviewProvider {
static var previews: some View {
SheetViewTest()
}
}
The error I am on the body getting is as follows:
Value of protocol type 'View' cannot conform to 'View'; only struct/enum/class types can conform to protocols, which is due to the .sheet modifier cause when I comment it out, the view works just fine...
Just remove closure return type (which is inferred automatically from returned content), i.e.
.sheet(item: $showSheet) { sheet in // << here !!
Text(sheet.rawValue)
}
Is there a better way to do this? Is there a way to access the UserDefaults in the environment?? I did the following:
struct ContentView: View {
#AppStorage("darkMode") var darkMode = false
var body: some View {
SubView(darkMode: $darkMode)
}
}
}
struct SubView: View {
#Binding var darkMode: Bool
var body: some View {
Text("Dark Mode is \(darkMode == true ? "on" : "off")")
}
}
By using #AppStorage in different views you still access the same UserDefaults.standard storage (unless you explicitly specify the suiteName).
Which means you can just use the #AppStorage directly in the subview.
struct ContentView: View {
#AppStorage("darkMode") var darkMode = DefaultSettings.darkMode
var body: some View {
VStack {
Button("Toggle dark mode") {
self.darkMode.toggle()
}
SubView()
}
.colorScheme(darkMode ? .dark : .light)
.preferredColorScheme(darkMode ? .dark : .light)
}
}
struct SubView: View {
#AppStorage("darkMode") var darkMode = DefaultSettings.darkMode
var body: some View {
Text("Dark Mode is \(darkMode == true ? "on" : "off")")
}
}
enum DefaultSettings {
static let darkMode = false
}
Note: the default darkMode value is extracted (to the DefaultSettings enum) so you don't repeat false in each view.
Alternatively you can inject #AppStorage directly to the environment. See:
Can #AppStorage be used in the Environment in SwiftUI?
Sorry for terrible wording, but in onAppear I am changing a State variable, however this does not re-render the view unless I use that State variable somewhere inside the view.
I am trying to pop a modal if the user is not logged in, but it won't pop unless I put the variable in the view like shown below
struct SearchView: View {
// Environment Variables
#EnvironmentObject var session: SessionStore
#State var sheetIsPresented: Bool = false
var body: some View {
NavigationView {
// Just having a line like this will cause it to work
if self.sheetIsPresented || !self.sheetIsPresented {}
}
.onAppear {
if self.session.userSession != nil {
self.sheetIsPresented = true
}
}
.sheet(isPresented: $sheetIsPresented) {
WelcomeSignInModal(sheetIsPresented: self.$sheetIsPresented)
}
}
}
You can try the following demo:
class SessionStore: ObservableObject {
var userSession: String? = "session"
}
struct ContentView: View {
#EnvironmentObject var session: SessionStore
#State var sheetIsPresented: Bool = false
var body: some View {
NavigationView {
Text("SearchView")
}
.onAppear {
self.sheetIsPresented = self.session.userSession != nil
}
.sheet(isPresented: $sheetIsPresented) {
Text("WelcomeSignInModal")
}
}
}
Tested with Xcode 11.6, iOS 13.6
Is it possible to depend on multiple conditions in SwiftUI? For example to show a sheet:
.sheet(isPresented: $stateA && $stateB, content: { ... }) // this is not working
Or is a different approach known?
no, it is not possible! isPresented accept Binding, that means the state is updated if sheet will be dismissed. Which of stateA, stateB have to be changed? or both of them? Even though someone will try to define && operator where left and right side is Binding, that is very bad idea. Don't try to do it!
Move the logic to your model, better outside of any View.
UPDATE (for Asperi)
this is valid code (with your extension)
struct ContentView: View {
#State private var isFirst = true
#State private var isSecond = false
var body: some View {
VStack {
Button("TestIt") {
self.isSecond = true
}
.sheet(isPresented: $isFirst && $isSecond) {
Text("A")
}
}
}
}
Try it! Pressing TestIt will open the sheet. There is no Button to "go back", but you can dismiss it with well known gesture. And try to press TestIt again ...
"I can only show you the door..." (c) Morpheus
Today is a day of overloaded operators :^) - previous was here, here is for your case (tested with Xcode 11.3+)
extension Binding where Value == Bool {
static func &&(_ lhs: Binding<Bool>, _ rhs: Binding<Bool>) -> Binding<Bool> {
return Binding<Bool>( get: { lhs.wrappedValue && rhs.wrappedValue },
set: {_ in })
}
}
struct TestCustomBinding: View {
#State private var isFirst = true
#State private var isSecond = false
var body: some View {
VStack {
Button("TestIt") {
self.isSecond = true
}
.sheet(isPresented: $isFirst && $isSecond) {
Button("CloseMe") {
// sheet MUST be closed explicitly via one of states !
self.isSecond = false
}
}
}
}
}
It is possible to get different conditions from a variable.
struct ChangingButton: View {
var text: String
var onButton: String
var offButton: String
var changeButton: Bool
var buttonCondition: String {
if isOn {
return isOnImage
} else {
return isOffImage
}
}
var body: some View {
Button(action: {
action()
}
, label: {
VStack {
Image(systemName: buttonCondition)
Text(text)
}
})
}
}
struct ChangingButton_Previews: PreviewProvider {
static var previews: some View {
ChangingButton(text: "My Button", onButton: "on", offButton: "off", changeButton: true, action: {
}).background(Color.black)
}
According to my understanding, if you define a view yourself (as a struct that implements View), then you can declare some var to be an Environment variable, like this:
#Environment(\.isEnabled) var isEnabled
This will give you access to the EnvironmentValues.isEnabled field.
However, it seems like this is only possible within the view definition itself.
Is it possible, given some view v, to get the environment object of that view? or get specific environment values?
I assume that taking into account that SwiftUI is reactive state managed framework then yes, directly you cannot ask for internal view environment value, actually because you can't be sure when that environment is set up for this specific view. However you can ask that view to tell you about its internal state, when view is definitely knowns about it... via binding.
An example code (a weird a bit, but just for demo) how this can be done see below.
struct SampleView: View {
#Environment(\.isEnabled) private var isEnabled
var myState: Binding<Bool>?
var body: some View {
VStack {
Button(action: {}) { Text("I'm \(isEnabled ? "Enabled" : "Disabled")") }
report()
}
}
func report() -> some View {
DispatchQueue.main.async {
self.myState?.wrappedValue = self.isEnabled
}
return EmptyView()
}
}
struct TestEnvironmentVar: View {
#State private var isDisabled = false
#State private var sampleState = true
var body: some View {
VStack {
Button(action: {
self.isDisabled.toggle()
}) {
Text("Toggle")
}
Divider()
sampleView()
.disabled(isDisabled)
}
}
private func sampleView() -> some View {
VStack {
SampleView(myState: $sampleState)
Text("Sample is \(sampleState ? "Enabled" : "Disabled")")
}
}
}
struct TestEnvironmentVar_Previews: PreviewProvider {
static var previews: some View {
TestEnvironmentVar()
}
}