Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

}
like image 640
Denzil95 Avatar asked Nov 26 '19 18:11

Denzil95


People also ask

How to show a alert in SwiftUI?

To show an alert, create some Boolean state that determines whether the alert should be visible, then attach that to an alert() modifier along with all the buttons you want to show in the alert. All buttons dismiss the alert when tapped, so you can provide an empty action for simple dismissal.

How does VStack work SwiftUI?

Using stacks in SwiftUI allows you to arrange multiple views into a single coherent view with certain properties. HStack allows to arrange its child views in a horizontal line. VStack allows to arrange its child views in a vertical line, and ZStack allows to overlap its child views on top of each other.

What is a VStack in Xcode?

A view that arranges its children in a vertical line.

What are stacks in SwiftUI?

A stack is a collection of SwiftUI views that are grouped together. You can use 3 kinds of stacks with SwiftUI: VStack, a vertical stack, which shows views in a top-to-bottom list. HStack, a horizontal stack, which shows views in a left-to-right list. ZStack, a depth-based stack, which shows views in a back-to-front ...


2 Answers

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 }))
        }
    }
}
like image 143
LuLuGaGa Avatar answered Oct 30 '22 04:10

LuLuGaGa


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!")
}
like image 36
Daniel Smith Avatar answered Oct 30 '22 04:10

Daniel Smith