88
votes

I'm trying to conditionally hide a DatePicker in SwiftUI. However, I'm having any issue with mismatched types:

var datePicker = DatePicker($datePickerDate)
if self.showDatePicker {
    datePicker = datePicker.hidden()
}

In this case, datePicker is a DatePicker<EmptyView> type but datePicker.hidden() is a _ModifiedContent<DatePicker<EmptyView>, _HiddenModifier>. So I cannot assign datePicker.hidden() to datePicker. I've tried variations of this and can't seem to find a way that works. Any ideas?

UPDATE

You can unwrap the _ModifiedContent type to get the underlying type using it's content property. However, this doesn't solve the underlying issue. The content property appears to just be the original, unmodified date picker.

8

8 Answers

103
votes

The simplest and most common way to hide a view is like the following:

struct ContentView: View {
    @State private var showText = true

    var body: some View {
        VStack {
            Button("Toggle text") {
                showText.toggle()
            }

            if showText {
                Text("Hello World!")
            }
        }
    }
}

This removes the Text view from the hierarchy when showText equals false. If you wish to have an option to preserve the space or want it as a modifier, see below.


I created an extension, so you can use a modifier, like so to hide the view:

Text("Hello World!")
    .isHidden(true)

Or for complete removal:

Text("Label")
    .isHidden(true, remove: true)

The extension below is also available on GitHub here if you want to use Swift Packages: GeorgeElsham/HidingViews.


Here is the code to create the View modifier:

I recommend you use this code in its own file (remember to import SwiftUI):

extension View {
    /// Hide or show the view based on a boolean value.
    ///
    /// Example for visibility:
    ///
    ///     Text("Label")
    ///         .isHidden(true)
    ///
    /// Example for complete removal:
    ///
    ///     Text("Label")
    ///         .isHidden(true, remove: true)
    ///
    /// - Parameters:
    ///   - hidden: Set to `false` to show the view. Set to `true` to hide the view.
    ///   - remove: Boolean value indicating whether or not to remove the view.
    @ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false) -> some View {
        if hidden {
            if !remove {
                self.hidden()
            }
        } else {
            self
        }
    }
}
93
votes

✅ The correct and Simplest Way:

You can set the alpha instead, this will preserve the layout space of the view too and does not force you to add dummy views like the other answers:

.opacity(isHidden ? 0 : 1)

Demo

Demo


💡 Cleaner Way! - Extend original hidden modifier:

Also, you can implement a custom function to get the visibility state as an argument:

extension View {
    func hidden(_ shouldHide: Bool) -> some View {
        opacity(shouldHide ? 0 : 1)
    }
}

Now just pass the bool to the modifier:

DatePicker($datePickerDate)
    .hidden(showDatePicker)

Note that unlike the original behavior of the hidden modifier, both of these methods preserve the frame of the hiding view.


⛔️ Don't use bad practices !!!

All other answers (including the accepted answer by @Jake) use branches instead of dependent code that cause a performance hit.

🛑 Branch example:

Branch

✅ Dependent Code example:

Dependent Code example

Returning logical SAME view for different states causes the SwiftUI to render engine to re-render and initial a view again and cause a performance hit! (see more at this WWDC session)

57
votes

Rather than dynamically setting a variable and using it in my view, I found that I was able to hide or show the date picker this way:

struct ContentView : View {
    @State var showDatePicker = true
    @State var datePickerDate: Date = Date()

    var body: some View {
        VStack {
            if self.showDatePicker {
                DatePicker($datePickerDate)
            } else {
                DatePicker($datePickerDate).hidden()
            }
        }
    }
}

Or, optionally, not including the date picker instead of hiding it:

struct ContentView : View {
    @State var showDatePicker = true
    @State var datePickerDate: Date = Date()

    var body: some View {
        VStack {
            if self.showDatePicker {
                DatePicker($datePickerDate)
            }
        }
    }
}
18
votes

Here is the simple way to Show/Hide view in SwiftUI.

  1. Add @State variable:

    @State var showLogo = false
    
  2. Add condition like below:

    VStack {
                if showLogo == true {
                Image(systemName: "house.fill")
                    .resizable()
                    .frame(width: 100, height: 100, alignment: .center)
                    .foregroundColor(Color("LightGreyFont"))
                    .padding(.bottom, 20)
                }
                Text("Real State App")
                    .font(Font.custom("Montserrat-Regular", size: 30))
            }.padding(.vertical, 25)
    
  3. Change state of your @State variable to Show/Hide the view like below:

    Button(action: {
                    withAnimation{
                        self.showLogo.toggle()
                    }
    
                }, label: {
                    Text("Login").font(.system(size: 20, weight: .medium, design: .default))
                        .frame(minWidth: 0, maxWidth: .infinity, maxHeight: 50)
                        .foregroundColor(Color("BlackFont"))
                        .cornerRadius(10)
    
                })
    

enter image description here

7
votes

For whoever needs it in the future, I created a ViewModifier which takes a Binding<Bool> as parameter so you can bind a boolean value to show and hide the view declaratively by just setting your showDatePicker: Bool variable.

All code snippets require import SwiftUI.

The ViewModifier:

struct Show: ViewModifier {
    @Binding var isVisible: Bool

    @ViewBuilder
    func body(content: Content) -> some View {
        if isVisible {
            content
        } else {
            content.hidden()
        }
    }
}

The function:

extension View {
    func show(isVisible: Binding<Bool>) -> some View {
        ModifiedContent(content: self, modifier: Show(isVisible: isVisible))
    }
}

And you can use it like this:

var datePicker = DatePicker($datePickerDate)
                     .show(isVisible: $showDatePicker)
6
votes

Command-click the view in question and select the Make Conditional option in Beta 5. I did this on one of my views (LiftsCollectionView), and it generated the following:

    if suggestedLayout.size.height > 150 {
      LiftsCollectionView()
    } else {
      EmptyView()
    }
2
votes

You also have the opacity modifier on any View:

ActivityIndicator(tint: .black)
   .opacity(self.isLoading ? 1.0 : 0.0)
0
votes

The following also works even without a placeholder view or calling hidden (iOS13.1 and Swift 5)

struct Foo: View {
    @State var condition: Bool

    var body: some View {
        if self.condition {
            Text("Hello")
        }
    }
}

It's hard to know exactly without peeking at the @ViewBuilder implementation, but when evaluating a conditional, it seems that we are getting an EmptyView if it fails by default.

So this is equivalent to some of the answers here, but it's simpler.