19

How do I toggle the presence of a button to be hidden or not?
We have the non-conditional .hidden() property; but I need the conditional version.

Note: we do have the .disabled(bool) property available, but not the .hidden(bool).

struct ContentView: View {
    var body: some View {
        ZStack {
            Color("SkyBlue")
            VStack {
                Button("Detect") {
                    self.imageDetectionVM.detect(self.selectedImage)
                }
                .padding()
                .background(Color.orange)
                .foreggroundColor(Color.white)
                .cornerRadius(10)
                .hidden() // ...I want this to be toggled.
            }
        }
    }
}
Frederick C. Lee
  • 9,019
  • 17
  • 64
  • 105
  • Look for this issue if it's what you're looking for. https://stackoverflow.com/questions/56490250/dynamically-hiding-view-in-swiftui – Valdes Aug 08 '19 at 20:51
  • Please, help others along with you. Post actual **searchable** code! Screenshots are really not good. Thanks! –  Aug 08 '19 at 22:26
  • Replaced image with code per feedback. – Frederick C. Lee Aug 12 '19 at 03:23
  • I agree with you Frederick... I do not see the point of having .hidden() if it isn't conditional , who would write the entire button code to just then hide it!? – Learn2Code Apr 21 '21 at 01:38

7 Answers7

21

I hope hidden modifier gets argument later, but since then, Set the alpha instead:

@State var shouldHide = false

var body: some View {
    Button("Button") { self.shouldHide = true }
    .opacity(shouldHide ? 0 : 1)
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • 3
    Until I tried it, I assumed (incorrectly) that this solution would only hide the button from view, but it would still be present & able to be clicked. That assumption is incorrect; the button with .opacity(0) can not be tapped, at least in the simulator. – ConfusionTowers Oct 11 '19 at 04:40
15

For me it worked perfectly to set the frame's height to zero when you do not want to see it. When you want to have the calculated size, just set it to nil:

SomeView
    .frame(height: isVisible ? nil : 0)

If you want to disable it in addition to hiding it, you could set .disabled with the toggled boolean.

SomeView
    .frame(height: isVisible ? nil : 0)
    .disabled(!isVisible)
laka
  • 644
  • 8
  • 23
  • This worked for me ! only thing I had to do extra was to set the width with the similar condition, since overall width of stack was getting affected – Ae Ri Mar 02 '22 at 08:12
6

You can utilize SwiftUI's new two-way bindings and add an if-statement as:

struct ContentView: View {

    @State var shouldHide = false

    var body: some View {
        ZStack {
            Color("SkyBlue")
            VStack {
                if !self.$shouldHide.wrappedValue { 
                    Button("Detect") {
                        self.imageDetectionVM.detect(self.selectedImage)
                    }
                    .padding()
                    .background(Color.orange)
                    .foregroundColor(Color.white)
                    .cornerRadius(10)
                }
            }
        }
    }
}

The benefit of doing this over setting the opacity to 0 is that it will remove the weird spacing/padding from your UI caused from the button still being in the view, just not visible (if the button is between other view components, that is).

Kyle Beard
  • 604
  • 5
  • 18
6

all the answers here works specifically for a button to be hidden conditionally.

What i think might help is making a modifier itself conditionally e.g: .hidden for button/view, or maybe .italic for text, etc..

Using extensions.

For text to be conditionally italic it is easy since .italic modifier returns Text:

extension Text {
    func italicConditionally(isItalic: Bool) -> Text {
        isItalic ? self.italic() : self
    }
}

then applying conditional italic like this:

@State private var toggle = false
Text("My Text")
    .italicConditionally(isItalic: toggle)

However for Button it is tricky, since the .hidden modifier returns "some view":

extension View {
    func hiddenConditionally(isHidden: Bool) -> some View {
        isHidden ? AnyView(self.hidden()) : AnyView(self)
    }
}

then applying conditional hidden like this:

@State private var toggle = false
Button("myButton", action: myAction)
    .hiddenConditionally(isHidden: toggle)
Esam Sherif
  • 327
  • 2
  • 7
  • 12
4

You can easily hide a view in SwiftUI using a conditional statement.

struct TestView: View{
    
    @State private var isVisible = false
    
    var body: some View{
        
        if !isVisible {
            HStack{
                Button(action: {
                    isVisible.toggle()
                    // after click you'r view will be hidden
                }){
                    Text("any view")
                }
            }
        }
        
    }
}
Roland Lariotte
  • 2,606
  • 1
  • 16
  • 40
  • Doesn't this then force the parent view to be rebuilt based on the value of isVisible? I've seen a lot of talk about view id, and just looking at what's happening in an app I'm building this seems to be capable of driving significant inefficiencies...? – Andy Davison Oct 19 '21 at 22:49
1

It isn't always going to be a pretty solution, but in some cases, adding it conditionally may also work:

if shouldShowMyButton {
    Button(action: {
        self.imageDetectionVM.detect(self.selectedImage)
    }) {
        Text("Button")
    }          
}

There will be an issue of the empty space in the case when it isn't being shown, which may be more or less of an issue depending on the specific layout. That might be addressed by adding an else statement that alternatively adds an equivalently sized blank space.

ConfusionTowers
  • 911
  • 11
  • 34
0
@State private var isHidden = true       
VStack / HStack
       if isHidden {
                            Button {
                                if !loadVideo(),
                                   let urlStr = drill?.videoURL as? String,
                                   let url = URL(string: urlStr) {
                                    player = VideoPlayerView(player: AVPlayer(), videoUrl: url)
                                    playVideo.toggle()
                                }
                            } label: {
                                Image(playVideo ? "ic_close_blue" : "ic_video_attached")
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(width: 50)
                            }
                            .buttonStyle(BorderlessButtonStyle())
                        }
    
     .onAppear {
                    if shouldShowButton {
                        isHidden = false
                    } else {
                        isVideoButtonHidden = true
                    }
                }
Gal Blank
  • 2,070
  • 2
  • 18
  • 18