Cesare
Cesare

Reputation: 9409

SwiftUI allow Toggle only on certain conditions

My app has paid features. I have a Toggle that the user can switch. If the user has not paid, then when the user tries to toggle the Toggle a sheet should be brought up (and the toggle should not be activated). If the user has paid, then the toggle can be toggled on and off without problems.

I can't understand how a simple struct (PaidFeature) can have access to an observable object (Model). How do I code that in SwiftUI?

class Model: ObservableObject {
    @Published var hasUserPaid: Bool = false
}

struct PaidFeature {
    var isEnabled = false
}

struct ContentView: View {
    @State private var feature = PaidFeature()
    @EnvironmentObject var model: Model

    
    var body: some View {
        Toggle(isOn: self.$feature.isEnabled) {
            Text("Hello, World!")
        }
    }
}

Upvotes: 1

Views: 2097

Answers (4)

홍성호
홍성호

Reputation: 71

You can use condition in body View

if self.model.hasUserPaid == true {
   Toggle(isOn: self.$feature.isEnabled)) {
      Text("Hello, World!")
   }
} else {
   Toggle(isOn: .constant({default Bool})) {
      Text("Hello, World!")
   }
}

Upvotes: 1

Mahdi BM
Mahdi BM

Reputation: 2104

Using Xcode 12, you can listen for changes of the toggle button using .onChange modifier, and whenever user toggles the button, you can toggle it back to the last state, and instead show a purchase this sheet if the user is not a premium user.

        .onChange(self.feature.isEnabled) { isEnabled in
                    if isEnabled && shouldShowPurchaseSheet {
                        // toggle back if you want the button to go to the inactive state
                        // show the sheet
                    }
                }

Upvotes: 2

Asperi
Asperi

Reputation: 257493

I assume it should be as

var body: some View {
    Toggle(isOn: self.$feature.isEnabled) {
        Text("Hello, World!")
    }
    .disabled(!model.hasUserPaid)     // << here !!
}

Update: demo of alternate variant with showing sheet. Tested with Xcode 12 / iOS 14

For simplicity all of demo all states are kept in view

struct DemoView: View {
    @State private var feature = false
    @State private var paid = false
    @State private var showPurchase = false

    var body: some View {
        Toggle(isOn: $feature) {
            Text("Hello, World!")
        }
        .allowsHitTesting(paid)
        .contentShape(Rectangle())
        .simultaneousGesture(TapGesture().onEnded {
            if !self.paid {
                self.showPurchase = true
            }
        })
        .sheet(isPresented: $showPurchase) {
            Text("Purchase this")
                .onDisappear {
                    self.paid = true
                }
        }
    }
}

Upvotes: 1

Frankenstein
Frankenstein

Reputation: 16341

You could add a disabled modifier to the Toggle.

struct ContentView: View {

    @State private var feature = PaidFeature()
    @EnvironmentObject var model: Model

    var body: some View {
        Toggle(isOn: self.$feature.isEnabled) {
            Text("Hello, World!")
        }.disabled(!model.hasUserPaid)
    }
}

Upvotes: 1

Related Questions