How do you make a Button conditionally hidden or disabled?
Asked Answered
G

8

22

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.
            }
        }
    }
}
Grillparzer answered 8/8, 2019 at 20:46 Comment(2)
Look for this issue if it's what you're looking for. #56490750Interosculate
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!?Spectrograph
R
14

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)
Repertory answered 6/6, 2020 at 12:51 Comment(1)
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 affectedNorm
P
24

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)
}
Promethium answered 8/8, 2019 at 20:53 Comment(1)
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.Gayla
R
14

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)
Repertory answered 6/6, 2020 at 12:51 Comment(1)
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 affectedNorm
S
10

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)
Satterwhite answered 7/8, 2020 at 11:26 Comment(3)
a perfect approach! we can use it also for other ViewKarlsbad
This should be the generally accepted solution as it uses the built-in .hidden() method without messing with opacity or frame sizes.Wylde
I haven't tested it but looks elegant. I would propose to make the extension's interface a little swiftier, like func hidden(if isHidden: Bool) so it reads like Button(...).hidden(if: myCondition)Kirchhoff
B
7

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).

Better answered 5/12, 2019 at 3:23 Comment(0)
D
6

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")
                }
            }
        }
        
    }
}
Dactylo answered 19/4, 2021 at 1:57 Comment(1)
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...?Sunup
G
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.

Gayla answered 11/10, 2019 at 4:35 Comment(0)
V
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
                    }
                }
Vargo answered 8/9, 2022 at 13:28 Comment(0)
T
0
struct Visibility: ViewModifier
{
    let isHidden: Bool
    
    func body(content: Content) -> some View {
        content
            .opacity(isHidden ? 0 : 1)
            .frame(height: isHidden ? 0 : nil)
    }
}

extension View
{
    func hidden(_if: Bool) -> some View {
        modifier(Visibility(isHidden: _if))
    }
}

This should work for other views too.

Button("I'm not visible") {
}
.hidden(_if: true)
Terse answered 19/12, 2023 at 14:6 Comment(1)
setting both - opacity and frame - modifiers is not neccessary.Kirchhoff

© 2022 - 2024 — McMap. All rights reserved.