How to create a clear background Button in SwiftUI - swiftui

I want the text of the button to be black but the button itself to be clear.
All of these variations lead to the same appearance (the default appearance!):
Button(action: {}) {
Text("Click me 0!").backgroundColor(.clear)
}
.background(Color.clear)
.backgroundColor(.clear)
Button(action: {}) {
Text("Click me 1!").backgroundColor(.clear)
}
.backgroundColor(.clear)
.background(Color.clear)
Button(action: {}) {
Text("Click me 0.0!")
}
.background(Color.clear)
.backgroundColor(.clear)
Button(action: {}) {
Text("Click me 1!")
}
.backgroundColor(.clear)
.background(Color.clear)
Button("click me 2") {
}
.background(Color.clear)
.backgroundColor(.clear)
Button("click me 3") {
}
.backgroundColor(.clear).background(Color.clear)

You need plain button style, like
Button("Demo") {
// action is here
}
.buttonStyle(PlainButtonStyle()) // << here !!

Related

Restructure NavigationLink to Button for LongPressGesture

I am trying to put an option for .onlongpressgesture for this list of groups. However, it doesn't seem to work and I figure, it will work on Button.
I have tried to apply ".onTapGesture" and ".onLongPressGesture", but there is no effect.
Is there a way I can transform the Code below from a NavigationLink to a Button with the same destination when tapped and an additional Menu (called "OptionsmenuView") when long pressed?
The NavigationLink:
VStack (spacing: 20){
ForEach(groups, id:\.self) { Group in
NavigationLink(destination: GroupView()) {
ZStack (alignment: .bottomLeading) {
Image(uiImage: (UIImage(data: Group.groupThumbnail ?? self.image) ?? UIImage(named: "defaultGroupThumbnail"))!)
.resizable(capInsets: EdgeInsets())
.aspectRatio(contentMode: .fill)
.frame(height: 200.0, alignment: .center)
.cornerRadius(22)
VStack (alignment: .leading) {
Text("\(Group.groupTitle ?? "Untitled")")
.font(.title)
.fontWeight(.heavy)
.multilineTextAlignment(.leading)
Text("Consists of 5 Flowers")
}
.padding([.leading, .bottom], 18.0)
.foregroundColor(.primary)
}
.listRowBackground(Color.black)
}
}
}
The OptionMenuView:
struct OptionsMenuView: View {
var body: some View {
Menu {
Button("Cancel", role: .destructive) {
// Do something
}
Button {
// Do something
} label: {
Label("Edit", systemImage: "square.and.pencil")
}
Button {
// Do something
} label: {
Label("Delete", systemImage: "trash")
}
} label: {
Label("Settings", systemImage: "gearshape.fill")
}
}
}
I appreciate any form of advice. Thanks in advance.
Couldn't you just use .contextMenu ?
NavigationView {
List (0..<10) { i in
NavigationLink {
Text("DestionationView")
} label: {
Text("Item \(i)")
}
.contextMenu { // << here
Button("Cancel", role: .destructive) {
// Do something
}
Button {
// Do something
} label: {
Label("Edit", systemImage: "square.and.pencil")
}
Button {
// Do something
} label: {
Label("Delete", systemImage: "trash")
}
}
}
}

SwiftUI: How to set foregroundColor on menu button

I would like to make a menu button red as it carries a destructive nature, but my implementation doesn't override the foreground color at all.
Code.
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button(action: {}) {
Label("Delete", systemImage: "trash")
.foregroundColor(.red)
}
}
label: {
Text("Next")
}
}
Other attempts:
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
Button(action: {}) {
Label("Delete", systemImage: "trash")
}
.foregroundColor(.red)
}
label: {
Text("Next")
}
}
Results
Noticed that the "Delete" button is not red.

SwiftUI: Menu inside list cell with onTapGesture triggers gesture

I have a SwiftUI List on iOS14.3 (Xcode12.3) and inside it's cells I want to have a Menu popup up when the user is tapping on a button. Additionally these cells have an onTapGesture and the problem is that this gesture is also triggered when the menu button is pressed.
Example:
List {
HStack {
Text("Cell content")
Spacer()
// Triggers also onTapGesture of cell
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
If you tap on the ellipsis image, the menu opens but also "Cell tapped" will be printed to the console. This is a problem for me, because in my real world example I am collapsing the cell within the tap gesture and of course I don't want that to happen when the user presses the menu button.
I found out, that when I long press on the button the gesture won't be triggered. But this is not acceptable UX in my opinion, took me a while to find that out for my self.
I also noticed, when I replace the Menu with a regular Button, then the cells gesture is not triggered while pressing (only with BorderlessButtonStyle or PlainButtonStyle, otherwise he is not active at all). But I don't know how to open a Menu from it's action.
List {
HStack {
Text("Cell content")
Spacer()
// Does not trigger cells onTapGesture, but how to open Menu from action?
Button(action: { print("Button tapped") }) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.buttonStyle(BorderlessButtonStyle())
}
.background(Color(.systemBackground))
.onTapGesture {
print("Cell tapped")
}
}
Alternatively, you can override Menu onTapGesture:
struct ContentView: View {
var body: some View {
List {
HStack {
Text("Cell content")
Spacer()
Menu(content: {
Button(action: {}) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: {}) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: {}) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
.onTapGesture {} // override here
}
.contentShape(Rectangle())
.onTapGesture {
print("Cell tapped")
}
}
}
}
Also, there's no need to use .background(Color(.systemBackground)) to tap on spacers. This is not really flexible - what if you want change background color?
You can use contentShape instead:
.contentShape(Rectangle())
I'm not sure if this is a bug or expected behavior, but instead of fighting with it I would recommend to avoid such overlapping (because even with success today it might stop working on different systems/updates).
Here is possible solution (tested with Xcode 12.1 / iOS 14.1)
List {
HStack {
// row content area
HStack {
Text("Cell content")
Spacer()
}
.background(Color(.systemBackground)) // for tap on spacer area
.onTapGesture {
print("Cell tapped")
}
// menu area
Menu(content: {
Button(action: { }) {
Text("Menu Item 1")
Image(systemName: "command")
}
Button(action: { }) {
Text("Menu Item 2")
Image(systemName: "option")
}
Button(action: { }) {
Text("Menu Item 3")
Image(systemName: "shift")
}
}) {
Image(systemName: "ellipsis")
.imageScale(.large)
.padding()
}
}
}

SwiftUI - Form Picker & Button Not Co-Existing

I have created a reusable component that has a picker, textfield and button within a form. However, with the button present, a tap on the picker field does not go to the picker. Rather it executes the button code. The TextField works fine. If I remove the button code, the proper behavior will occur with the picker. So the question is how to have both elements within this component? Please note that the preview adds the Navigation and Form which would otherwise come from the parent view.
var body: some View {
HStack {
if !showFee {
Spacer()
Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
self.showFee.toggle()
}
}) {
Image(systemName: "plus.circle")
.font(.largeTitle)
}
Spacer()
} else {
VStack(spacing:20) {
Picker(selection: $feeSelection, label: Text("Fee Type")) {
ForEach(0 ..< fees.count) {
Text(self.fees[$0])
}
}
TextField("Fee Amount: $", value: $feeAmount, formatter: NumberFormatter.currency)
.keyboardType(.decimalPad)
Divider()
Button(action: {
withAnimation(.easeInOut(duration: 0.3)) {
self.showFee.toggle()
}
}) {
Image(systemName: "trash.circle.fill")
.font(.largeTitle)
.foregroundColor(.red)
}
}
}
}
.padding()
}
}
struct FeeCell_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
Form {
FeeCell()
}
}
}
}
What you can do is to apply the PlainButtonStyle to your button. This will stop the button's tap covering to the whole cell in the Form:
Button(action: {}) {
Text("Button")
}
.buttonStyle(PlainButtonStyle())

How to manually show contextMenu in SwiftUI?

I would like to show contextMenu by clicking on the left mouse button?
How to manually show view in SwiftUI?
Image(systemName: "book")
.contextMenu {
Text("something1")
Text("something2")
Text("something3")
}
You can use MenuButton with a menuButtonStyle to create a button which, when clicked, shows a menu. Seems to be Mac only currently.
MenuButton("Menu") {
Button(action: {
print("Clicked an item")
}) {
Text("Menu Item Text")
}
}.menuButtonStyle(BorderlessButtonMenuButtonStyle())
The currently accepted answer uses MenuButton, which is now deprecated. Below is how to use the new Menu option.
This is possible now via Menu (iOS 14+, MacOS 11+)
Menus are covered in this WWDC video at ~11:15.
Playground Example:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View {
HStack {
// Other views
Text("Example View 1")
// Button, that when tapped shows 3 options
Menu {
Button(action: {
}) {
Label("Add", systemImage: "plus.circle")
}
Button(action: {
}) {
Label("Delete", systemImage: "minus.circle")
}
Button(action: {
}) {
Label("Edit", systemImage: "pencil.circle")
}
} label: {
Image(systemName: "ellipsis.circle")
}
}
.frame(width: 300, height: 300, alignment: .center)
}
}
PlaygroundPage.current.setLiveView(ContentView())
If you wish to support MacOS 10.15 you could do something like:
if #available(iOS 14, macOS 11, *) {
Menu {
Button(action: {
}) {
Label("Add", systemImage: "plus.circle")
}
Button(action: {
}) {
Label("Delete", systemImage: "minus.circle")
}
Button(action: {
}) {
Label("Edit", systemImage: "pencil.circle")
}
} label: {
Image(systemName: "ellipsis.circle")
}
} else if #available(macOS 10.15, *) {
MenuButton(
label: Image(nsImage: NSImage(named: NSImage.actionTemplateName)!),
content: {
Button(action: {}) {
HStack {
Image(nsImage: NSImage(named: NSImage.addTemplateName)!)
Text("Add")
}
}
Button(action: {}) {
HStack {
Image(nsImage: NSImage(named: NSImage.removeTemplateName)!)
Text("Delete")
}
}
Button(action: {}) {
HStack {
Image(nsImage: NSImage(named: NSImage.refreshTemplateName)!)
Text("Edit")
}
}
})
.menuButtonStyle(BorderlessButtonMenuButtonStyle())
} else {
// Custom code here would be needed to support iOS 13 and MacOS 10.14
}