Denzil
Denzil

Reputation: 445

How to make a VStack Clickable to show alert in SwiftUI

I have a VStack with image and a text, I need to make it clickable so I can show and alert to the user. I have tried onTapGesture method but I'm able to print the statement but alert is not showing up.

Is there any alternate way to get the solution?

I have added the whole swift file for your reference

Code

@State private var hasOneConnected: Bool = false
@State private var showConnectionAlert = false

private var connectionAlert: Alert {
    Alert.init(title: Text("Oops!"), message: Text("There is an error."), dismissButton: .default(Text("OK")))
}
var body: some View {
    VStack(alignment: .center, spacing: 0) {

        // MARK: - Header
        VStack(alignment: .center, spacing: 0) {
            Spacer().frame(height: 55)
            HStack(alignment: .center, spacing: 25) {
                Spacer()
                Image("Logo")
                Spacer(minLength: 5)
                Text("Zone Control")
                    .foregroundColor(.white)
                Spacer()
            }
            Spacer()
        }
        .frame(height: 100, alignment: .center)
        .background(
            LinearGradient(
                gradient: Gradient(colors: [Color.Heat.primary, Color.Heat.primary.opacity(0.8), Color.Heat.primary.opacity(0.5)]),
                startPoint: .top, endPoint: .bottom
            )
        )
        // MARK: - Menu Bar
        HStack(alignment: .center, spacing: 10) {
            Spacer().frame(maxWidth: 20)

            HStack(alignment: .center, spacing: 10) {
                Image("batteryModule")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 8)
                Text("Pod Status")
                    .font(.caption)
                    .foregroundColor(.white)
            }

            Spacer()

            VStack(alignment: .center, spacing: 4) {
                Image(self.hasConnected ? "bluetoothConnected" : "bluetoothNotConnected")
                Text(self.hasConnected ? "Connected" : "Disconnected")
                    .font(.caption)
                    .foregroundColor(.white)
            }
            .frame(width: 80)

            VStack(alignment: .center, spacing: 4) {
                Image("batteryStatus")
                Text("\(self.batteryLevel)%")
                    .font(.caption)
                    .foregroundColor(.white)
            }
            .frame(width: 60)

            Spacer().frame(maxWidth: 10)
        }
        .padding()
        .background(Color.black)

    }

    .statusBar(hidden: true)
    .edgesIgnoringSafeArea(.all)
    .onAppear(perform: {
        UserDefaults.standard.set(true, forKey: "onboarding")
        self.update()
    })
        .onReceive(self.skiinBLE.objectWillChange) { _ in
            self.update()
    }

}

func update() {
    self.hasOneConnected = self.topLayer.cbPeripheral?.state != .disconnected
    self.batteryLevel = self.topLayer.moduleInformation?.batteryLevel.rawValue ?? 0

}

Upvotes: 22

Views: 13288

Answers (2)

Daniel Smith
Daniel Smith

Reputation: 960

For making empty containers tappable (for example Spacer()), you should use .contentShape(Rectangle()) modifier:

VStack(spacing: 4) {
  Image(systemName: "antenna.radiowaves.left.and.right")
  Text("Connected")
  Spacer()
}
.contentShape(Rectangle())
.onTapGesture {
  print("The whole VStack is tappable now!")
}

Upvotes: 30

LuLuGaGa
LuLuGaGa

Reputation: 14408

I have simplified your code to just bare essentials. You don't really need to add a tap gesture, you can wrap the whole element (e.g. a VStack) in a Button and handle triggering the alert from there. Important bit is to remember to set showConnectionAlert back to false when the user taps OK on the Alert. The side effect of wrapping it in a Button is that everything inside will be rendered in the tint colour. That's why I have applied .foregroundCololor() to the VStack (with some images you might also have to add .renderingMode(.original) modifier):

struct ContentView: View {

    @State private var showConnectionAlert = false

    var body: some View {
        Button(action: { self.showConnectionAlert = true }) {
            VStack(spacing: 4) {
                Image(systemName: "antenna.radiowaves.left.and.right")
                Text("Connected")
            }   .foregroundColor(.primary)
        }
            .alert(isPresented: $showConnectionAlert) {
                    Alert(title: Text("Nice"),
                          message: Text("The alert is showing!"),
                          dismissButton: Alert.Button.default(Text("OK"),
                                                              action: { self.showConnectionAlert = false }))
        }
    }
}

Upvotes: 7

Related Questions