SwiftUI Button is not grayed out when tapping - ios

I am really trying to understand something.
I want to create a button with some text on it. The button have a frame and a color (color is same as background). When I tap that button I want the some effect to produce (some kind of discolouration to entire button). If the color is the same as the background just the text is coloured different during the tap. If I have a button with red background the effect is produced to the whole button. I have attached a picture on how things should look.
Before tap button should look like this:
When the user press the button (without release) the button looks like this:
In my case the red rectangle color is the same as the background color.But I want to have the same rectangle overlay. Instead of this I obtain the following.
Before tap:
During the tap:
In this case only the number has some sort of overlay. But the rectangle not.
Here is the code. What should I change to obtain the same feedback if the button is coloured the same as the background?
Button(action: {
some action
}){
Text("1")
.font(.title)
.foregroundColor(.blue)
.frame(width: 80, height: 48)
.background(Color.white)
.cornerRadius(4)
}

I cannot say that understood what exactly you want to achieve, but different visual feedback on button pressed can be done with help of button style. Within a style we have access to isPressed state, so can conditionally change presentation of button depending on that.
Here is some demo that should be helpful
Button(action: {
some action
}){
Text("1")
}
.buttonStyle(MyButtonStyle())
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.font(.title)
.foregroundColor(configuration.isPressed ? .white : .blue) // << ex. !!
.frame(width: 80, height: 48)
.background(configuration.isPressed ? Color.clear : Color.white) // << ex. !!
.cornerRadius(4)
}
}

I did not understand the question very well, however what I can tell is that you have a button that has a background the same as your main background, and you want the effect of when the user press the button, why not adding some Shadow?
Button(action: {
}){
Text("1")
.font(.title)
.foregroundColor(.blue)
.frame(width: 80, height: 48)
.background(Color.white)
.cornerRadius(4)
.shadow(color: Color.black.opacity(0.5), radius: 2, x: 0, y: 0)
}
The result as you said:

I understood your question. I think button pressed effect adopts color according to background color so I have just hacked this trick. In my code I have just used one ZStack and give white background color like this :
struct ContentView: View {
var body: some View {
ZStack{
Color.red
ZStack{
Rectangle().fill(Color.white)
Button(action: {
// some action
}){
Text("1")
.font(.title)
.frame(width: 80, height: 48, alignment: .center)
.background(Color.red)
}//.buttonStyle(MyButtonStyle()) // Set style if you want custom color on press event and remove background color from text.
}.frame(width: 80, height: 48, alignment: .center).cornerRadius(4)
}
}
}
struct MyButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.background(configuration.isPressed ? Color.blue.opacity(0.5) : Color.red)
}
}
If you want to add your own color on the press then set button style and remove the background color from Text.
Hope this useful :)

Related

Questions about SwiftUI Layout

I tried to place a Button inside a TextField
List {
Section(header: Text("ADD WORD")) {
HStack {
TextField("input word", text: $dictionaryVM.inputText)
.autocapitalization(.none)
.disableAutocorrection(true)
Button(action: {
showDetailView = true
}, label: {
Image(systemName:"plus")
})
.frame(width: 40, height: 40)
.background(Color.blue.opacity(0.1))
.cornerRadius(10)
.padding(.trailing, -10)
}
}
}
I draw a red border on the screenshot 1 image, when I click outside that red border, it'll trigger the button action
and When I click the button, like the screenshot 2 showed, the whole TextField turned gray, I don't know what I did is wrong, please help me out.

onDrag preview isn’t just of the dragged view but also displays the background the view is laid on top of

I’m using an .onDrag modifier on a view that has rounded corners:
struct RootView: View {
#State var dragging: Bool = false
var body: some View {
VStack {
Color.red.cornerRadius(32)
.frame(width: 300, height: 300)
.onDrag {
dragging = true
return NSItemProvider(object: NSString())
}
}.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.green)
}
}
The problem is when I invoke a drag - due to the automatic drop shadow effect applied by the system - it doesn’t feel as though I’m dragging the card on its own. See below.
Is there any way to get the card to look dragged with a transparent background, as opposed to above whatever background colour it was laid on top of?
There is content shape type for this,
Color.red.cornerRadius(32)
.frame(width: 300, height: 300)
.contentShape(.dragPreview, RoundedRectangle(cornerRadius: 32)) // << here !!
.onDrag {

SwiftUI: add Text below a Button

So I'm building my first App and trying to get some Text below the Button, but it should still be a part of the button. So thats what i got:
Button{
print("tapped")
} label: {
Image("Wand")
.resizable()
.frame(width: 100, height: 100)
}
.font(.title3)
.background(Color.gray)
.foregroundColor(.black)
.cornerRadius(20)
It looks like this:
But I want is this:
I've tried putting a VStack into the Label, but the Text stays in the gray button
I think it may help you
public struct VerticalLabelStyle: LabelStyle {
public let spacing: CGFloat
public init(spacing: CGFloat = 8) {
self.spacing = spacing
}
public func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .center, spacing: spacing) {
configuration.icon
configuration.title
}
}
}
public struct ContentView: View {
#State private var text: String = ""
public var body: some View {
Label {
Text(text)
} icon: {
Image(systemName: "gamecontroller.fill")
.resizable()
.frame(width: 44, height: 44, alignment: .center)
}
.frame(width: 100, height: 100)
.labelStyle(VerticalLabelStyle())
.onTapGesture(perform: onTap)
}
func onTap() {
text = "tapped"
}
}
If you have some questions please ask
Your idea with a VStack is the correct way. You just have to keep in mind where to place your modifiers. In your case you placed them on the button level. Meaning they are applied to the entire label, which is the reason why your Text is inside the gray background. Just update also the place of your modifiers inside your VStack.
Here is are short example:
Button {
// Your button action
} label: {
VStack {
RoundedRectangle(cornerRadius: 25)
.frame(width: 100, height: 100)
.padding()
.background(Color.black) // new position of background modifier that is only applied on your Image (here a rectangle)
Text("Foo")
.font(.title3)
.foregroundColor(.black)
// These modifiers could stay on button level, but I moved them here to explicitly show where they applied
}
}
Keep in mind you can put any view inside the label of your button and that in this case the Text is part of the button and will trigger the action if a user taps on it. If you only want that the Image is the tap action wrap your button inside a VStack containing only the Image and a Text as the second part of the Stack like this:
VStack {
Button {
// Your button action
} label: {
VStack {
RoundedRectangle(cornerRadius: 25)
.frame(width: 100, height: 100)
.padding()
.background(Color.black)
}
}
Text("Foo")
.font(.title3)
.foregroundColor(.black)
}

How to animate the navigation link on press in SwiftUI?

I'm trying to improve UX by giving some feedback when the NavigationLink() is pressed.
By this I mean a simple animation that grows then shrinks the link to show that it was pressed or any other way to provide feedback.
This is the code I'm trying to improve:
NavigationLink(
destination: TrickView(trickId: begginerTricks[index]["trickId"] as! String),
label: {
TrickRowView(name: begginerTricks[index]["trickName"] as! String,
trickType: begginerTricks[index]["trickType"] as! String,
trickComplete: [false,false,false,false],
width: width * 0.73, height: height * 0.13)
})
.padding(.bottom, 15)
This is one NavigationLink in a list of navigation links.
Any help on how to do this would be appreciated.
There are many ways to add animation to your navigation link.
Here is one of them.
You can create ButtonStyle and apply it to the navigation link with the use of scaleEffect, background, or you can also add additional to as per your choice.
ButtonStyle :
struct ThemeAnimationStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.font(.title2)
.foregroundColor(Color.white)
.frame(height: 50, alignment: .center)
.background(configuration.isPressed ? Color.green.opacity(0.5) : Color.green)
.cornerRadius(8)
.shadow(color: Color.gray, radius: 10, x: 0, y: 0)
.scaleEffect(configuration.isPressed ? 0.9 : 1.0) //<- change scale value as per need. scaleEffect(configuration.isPressed ? 1.2 : 1.0)
}
}
How to use:
var body: some View {
NavigationView {
NavigationLink(
destination: Text("Destination view"),
label: {
Text("MyButton")
.padding()
})
.buttonStyle(ThemeAnimationStyle())
}
}

Expand SwiftUI button tap area over the button frame

I read a similar question about that issue. But that only expand the button size use .padding(). The red region in the snapshot is the tappable area.
The trailing padding to screen edge inserted by SwiftUI.framework can not be removing (I think). That causes any tap in the right edge of the screen can not trigger button action. How can I make the tappable area beyond the button size? So that user could tap that button more easier.
Thanks.
.navigationBarItems(trailing:
Button(action: {
self.context.isSheetDisplay = true
}, label: {
Image(systemName: "square.and.pencil")
.font(.system(size: 20))
})
.padding([.leading, .top, .bottom])
.background(Color.red)
)
Update:
e.g.
The Photos.app in simulator could trigger tap at the edge.
My solution to this issue for iOS is to add a nearly transparent background color ViewModifier to the button as part of a ButtonStyle:
import SwiftUI
struct ExampleBigPaddedButton: View {
var body: some View {
Button("Tap anywhere inside") {
print("Tapped")
}.buttonStyle(BigPaddedButtonStyle())
}
}
struct BigPaddedButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
return configuration
.label
.foregroundColor(configuration.isPressed ? .gray : .black)
.padding(EdgeInsets(top: 75, leading: 25, bottom: 75, trailing: 25))
.background(Color(red: 1, green: 1, blue: 1, opacity: 0.01))
.background(Rectangle().stroke(Color.gray))
}
}
struct ExampleBigPaddedButton_Previews: PreviewProvider {
static var previews: some View {
ExampleBigPaddedButton()
}
}
This produces a button having any size of padding you need that is tappable anywhere within its bounds.
The hit test area includes the background, but only when it isn’t clear. If your button is overlaying an area with a uniform background, you can make its background not clear, while still not noticeable to the user, by setting its background to the background color.
Button(…)
.background(.background)
So basically you need to give the label of the Button a padding and then give the button an offset with this padding. This is how I did it:
Button {
print("Tapped")
} label: {
Image(systemName: "xmark.circle.fill")
.foregroundColor(.grayColor.opacity(0.7))
.padding(EdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15))
}
.background(.red) // Shows the tappable area
.offset(x: 20, y: -20)

Resources