This seems like maybe a simple question, but I'm looking for a way to make one (or more, I suppose) buttons in an ActionSheet in SwiftUI to be disabled. For example, if something is illegal based on the state of the app, I don't want to let the user click the button - indeed, I might change the text to explain why it's disabled.
Xcode autocomplete isn't really turning anything up for me, and the docs (here and here) aren't giving me anything. Is my best/only option simply not putting the button in the list of buttons in the ActionSheet? (And kinda letting the user infer why it's not there...)
Thanks so much for any advice!
My simple code:
private func getActionSheet() -> ActionSheet {
let buttons: [ActionSheet.Button] = [
.default(Text("This is fine...")) { foo() },
.default(Text("This is forbidden - disable me!")) { foo() },
.cancel()
]
return ActionSheet(title: Text("Do it!"), buttons: buttons)
}
For nondestructive actions you can simply show a Menu. They support the .disabled(true) modifier.
var body: some View {
Menu("Do it!") {
Button("This button works fine", action: { })
Button("This one is disabled", action: { })
.disabled(true)
}
}
Related
I'm trying to modify the color of the Image in the swipe action of a list in SwiftUI.
I have tried a few things:
Setting the image with rendering template and after that applied the foregroundColor modifier
Setting foregroundColor modifier directly to button
Tried to set a custom image
I searched a lot for this, and I didn't found anything, not only didn't found solution, I didn't found a NO, like "You can't do that", so this question is for me and anyone in the future trying to do the same thing and they being able to found a quick solution or a quick "Thats impossible".
A piece of my code, really simple:
List {
if viewModel.showSkeleton {
ForEach(0...4, id: \.self) { _ in
SkeletonTransactionRowView().listRowSeparator(.hidden)
}
} else {
ForEach(viewModel.transactions) { transaction in
TransactionRowView(transaction: transaction)
.swipeActions {
Button { viewModel.delete(transaction: transaction) } label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
.listRowSeparator(.hidden)
}
}
.listStyle(PlainListStyle())
NOTE: I know i can make a custom drag gesture and do all this stuff manually, but I want to know if it's possible to do it with the swipe action modifier.
In a SwiftUI app I have a few buttons (let us say 3 as an example). One of them is highlighted.
When I tap on a non-highlighted button, the previously highlighted button toggles to non-highlighted and the tapped button becomes highlighted. If I tap the already highlighted button, nothing happens.
This scenario is simple and I have a highlighBtn variable, when a button is tapped highlighBtn takes the value of the tapped button. This variable is then used when the next tap happens to toggle off the previously highlighted button.
This cycle is OK, but the problem is when I do the first tap. For some reasons, things don't work.
This is how I handle the creation of the highlighBtn variable:
class ActiveButton: ObservableObject {
#Published var highlighBtn = Button(....)
}
#StateObject var box = ActiveButton()
Here is the relevant code, when the button is tapped:
#EnvironmentObject var box: ActiveButton
....
Button(action: {
// Toggle off the previously highlighted button.
box.highlighBtn.highLight = false
.... some useful processing ....
box.highlighBtn = self
})
One detail I should give: if I tap the highlighted button to start, then all works as it should.
I have tried various method to solve this apparently simple problem but failed.
The first method was to try to initialize the highlighBtn variable.
The second was to try to simulate a tap on the highlighted button.
I must be missing something simple.
Any tip would be welcome.
After further investigation .....
I have created a demo app to expose my problem.
Since I lack practice using GitHub, it took me some time, but it is now available here.
For that I created a SwiftUI app in Xcode.
In SceneDelegate.swift, the four lines of code right after this one have been customized for the needs of this app:
// Create the SwiftUI view that provides the window contents.
Beside that all I did resides inside the file ContentView.swift.
To save some time to anyone who is going to take a look; here is the way to get to the point (i.e. the issue I am facing).
Run the app (I did it on an iPhone 7). You will see seven buttons appear. One of them (at random) will be highlighted. Starting with the highlighted button, tap on a few buttons one after another as many times as you want. You will see how the app is supposed to work.
(After switching it off) Run the app a second time. This time you will also tap on a few buttons one after another as many times as you want; but start by tapping one of the non-highlighted button. You will see the problem appear at this point.
Here is a solution for the first part of your question: Three buttons where the last one tapped gets highlighted with a background:
import SwiftUI
struct ContentView: View {
enum HighlightTag {
case none, first, second, third
}
#State private var highlighted = HighlightTag.none
var body: some View {
VStack {
Button("First") {
highlighted = .first
}.background(
Color.accentColor.opacity(
highlighted == .first ? 0.2 : 0.0
)
)
Button("Second") {
highlighted = .second
}.background(
Color.accentColor.opacity(
highlighted == .second ? 0.2 : 0.0
)
)
Button("Third") {
highlighted = .third
}.background(
Color.accentColor.opacity(
highlighted == .third ? 0.2 : 0.0
)
)
}
}
}
Update:
After reviewing your sample code on GitHub, I tried to understand your code, I tried to make some simplifications and tried to find a working solution.
Here are some opinions:
The Attribute "#State" in front of "var butnsPool" is not needed and confusing.
The Attribute "#State" in front of "var initialHilight" is not needed and confusing.
Your ActiveButton stores a copy of the selected Button View because it is a struct which is probably the main reason for the strange behaviour.
The needInit in your ObservableObject smells bad at least. If you really need to initialize something, you may consider doing it with some .onAppear() modifier in you ContentView.
There is probably no need to use .environmentObject and #EnvironmentObject. You could consider using a parameter and #ObsservedObject
There is probably no need for the ActiveButton at all, if you only use it internally. You could consider using a #State with the selected utton name
Your BtnTxtView is fine, but consider replacing the conditional (func if) with some animatable properties, if you want to animate the transition.
Based on your code I created a much simpler and working solution.
I removed the ActiveButton class and also the BttnView struct.
And I replaced the ContentView with this:
struct ContentView: View {
var butnsPool: [String]
var initialHilight: Int
#State var selectedBox: String = ""
var body: some View {
ForEach(butnsPool, id: \.self) { buttonName in
Button(action: {
selectedBox = buttonName
})
{
BtnTxtView(theLabel: buttonName,
highLight: buttonName == selectedBox)
}
}.onAppear {
selectedBox = butnsPool[initialHilight]
}
}
}
I have a .navigationBarItems button that I want to enable/disable depending on what my app is doing but I cannot for the life of me figure out how the heck to do this?!
My view is being pulled into my main app view via a NavigationView so I know the bar items are being shown correctly and whatnot. On my view that's being pulled in upon navigating to it I have this code at the bottom of the stack:
.navigationBarItems(
trailing:
Button("End Day") {
//do something here
scheduleEndDayNotificatons()
}
)
I'd like to be able to toggle the trailing "End Day" button to be enabled/disabled, probably based on some Bool state variable? Such as when the user enters into a mode of the app it's enabled, when they exit that mode it's disabled. My app is an exercise app, so when they're working out I'd want it to be enabled and when they finish working out it's disabled (which is the default).
Any help would be very much appreciated! Thank you!!
We can use modifier .disabled applied to Button and link it with some state variable, like
#State private var isDisabled = false
// ... other code
.navigationBarItems(
trailing:
Button("End Day") {
//do something here
scheduleEndDayNotificatons()
}
.disabled(isDisabled)
)
I'm creating a list with custom rows in SwiftUI. When I tap one of them, I want the row to turn light gray until another tap is detected.
This doesn't happen with simple non-custom lists either.
Here's my list:
List{
ForEach(blocks){ block in
BlockRow(block: block)
}
.onDelete(perform: delete)
.onMove(perform: day.move)
}
When I tap on one of the items, nothing happens. If I create a simple list with storyBoards, I get the behavior I want:
Hey so you asked this 3 months ago so I hope you got an answer somewhere or figured it out since then, but to get to the good stuff to make a button tappable I was able to get it working using this,
List {
Button (action: {
//Whatever action you want to perform
}) {
//Code to present as the cell
}
}
I would maybe try the following based on your code,
List (blocks) { block in
Button (action: {
//perform button action
}) {
//How the cell should look
BlockRow(block: block)
}
}
.onAppear()
.onDelete()
When I used third party SCLAlertView there was a problem actually there is a problem that is I want to perform some action when the button will pressed but there is just the customization properties but I am wondering for the action scope can someone help me out?
you can use this
let appearance = SCLAlertView.SCLAppearance(
showCloseButton: false // if you dont want the close button use false
)
let alertView = SCLAlertView(appearance: appearance)
alertView.addButton("Ok Pressed") {
print("Ok button tapped")
}
alertView.showSuccess("Success", subTitle: "")
you get the detail example for add buttons and hide default close buttons property in SCLAlertView
I never used this library, however if we take a look at the Github repo of the project (https://github.com/vikmeup/SCLAlertView-Swift) we will see the following example:
alert.addButton("Show Name") {
print("Text value: \(txt.text)")
}
Where print("Text value: \(txt.text)") gets executed after clicking the button.