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.
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 {
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 :)
I'm trying to set a rounded border to a button but the border of the button is not correct.
Code:
Button(action: {
print("sign up bin tapped")
}) {
Text("SIGN UP")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
}
.border(Color.white, width: 2)
.cornerRadius(25)
Output:
As you can see the border at corner are cut-off.
Any suggestion what am I doing wrong?
Instead of setting the cornerRadius to the Button use an overlay for the inside View:
Edit: If you have a background for the button you also need to apply the cornerRadius to the background.
Button(action: {
print("sign up bin tapped")
}) {
Text("SIGN UP")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 25)
.stroke(Color.white, lineWidth: 2)
)
}
.background(Color.yellow) // If you have this
.cornerRadius(25) // You also need the cornerRadius here
Updated for Swift 5 & iOS 13.4+ with Press States!
None of the examples worked for buttons with both dark and white background colors as well as none of them had press state updates, so I built this LargeButton view that you can see below. Hope this helps, should be pretty simple to use!
Example Photos
Example Use
// White button with green border.
LargeButton(title: "Invite a Friend",
backgroundColor: Color.white,
foregroundColor: Color.green) {
print("Hello World")
}
// Yellow button without a border
LargeButton(title: "Invite a Friend",
backgroundColor: Color.yellow) {
print("Hello World")
}
Code
struct LargeButtonStyle: ButtonStyle {
let backgroundColor: Color
let foregroundColor: Color
let isDisabled: Bool
func makeBody(configuration: Self.Configuration) -> some View {
let currentForegroundColor = isDisabled || configuration.isPressed ? foregroundColor.opacity(0.3) : foregroundColor
return configuration.label
.padding()
.foregroundColor(currentForegroundColor)
.background(isDisabled || configuration.isPressed ? backgroundColor.opacity(0.3) : backgroundColor)
// This is the key part, we are using both an overlay as well as cornerRadius
.cornerRadius(6)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(currentForegroundColor, lineWidth: 1)
)
.padding([.top, .bottom], 10)
.font(Font.system(size: 19, weight: .semibold))
}
}
struct LargeButton: View {
private static let buttonHorizontalMargins: CGFloat = 20
var backgroundColor: Color
var foregroundColor: Color
private let title: String
private let action: () -> Void
// It would be nice to make this into a binding.
private let disabled: Bool
init(title: String,
disabled: Bool = false,
backgroundColor: Color = Color.green,
foregroundColor: Color = Color.white,
action: #escaping () -> Void) {
self.backgroundColor = backgroundColor
self.foregroundColor = foregroundColor
self.title = title
self.action = action
self.disabled = disabled
}
var body: some View {
HStack {
Spacer(minLength: LargeButton.buttonHorizontalMargins)
Button(action:self.action) {
Text(self.title)
.frame(maxWidth:.infinity)
}
.buttonStyle(LargeButtonStyle(backgroundColor: backgroundColor,
foregroundColor: foregroundColor,
isDisabled: disabled))
.disabled(self.disabled)
Spacer(minLength: LargeButton.buttonHorizontalMargins)
}
.frame(maxWidth:.infinity)
}
}
Official .bordered modifier support in iOS 15+
Buttons now have baked in border styling support using the .buttonStyle(.bordered) modifier. I would suggest using the corner radius Apple provides for these buttons for the best platform-specific styling. We can change the color to be consistent with the system styles for buttons and tint the background as well as text using the .tint modifier:
Button("Add") { ... }
.buttonStyle(.bordered)
.tint(.green)
You can make the tint color more prominent (bolder) using .borderedProminent and control the size using .controlSize:
Button("food") { ... }
.tint(.red)
.controlSize(.small) // .large, .medium or .small
.buttonStyle(.borderedProminent)
You can also use this modifier on parent Views of Buttons and toggle lighter color schemes using .accentColor in child Buttons:
ScrollView {
LazyVStack {
Button("Test Button 1") { ... }
.buttonStyle(.borderedProminent)
.keyboardShortcut(.defaultAction) // Tapping `Return` key actions this button
Button("Test Button 2") { ... }
.tint(.accentColor)
}
}
.buttonStyle(.bordered)
.controlSize(.large)
Advice
Apple for some reason doesn't like single-line bordered buttons which is why the .border() modifier was deprecated in Xcode 12. With this change, I suggest developers avoid creating single-line bordered buttons because they now are not preferred in Apple's Human Interface Guidelines. Using prominent buttons everywhere also violates HIG.
Extra NOTE: Apple's .bordered style provides the standard platform style across device types. In addition, the Button responds to Dark Mode dynamically and scales its size with Dynamic Type (native accessibility support).
Swift 5 & iOS 14 – Borders also react when pressed
struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(5)
.foregroundColor(configuration.isPressed ? Color.red.opacity(0.5) : .red)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(configuration.isPressed ? Color.red.opacity(0.5) : .red, lineWidth: 1.5)
)
}
}
How to use
Button("Hide") {
print("tapped")
}.buttonStyle(PrimaryButtonStyle())
borders also react when pressed
Xcode 11.4.1
Button(action: self.action) {
Text("Button Name")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(.white)
.padding(10)
.background(Color.darkGray)
.cornerRadius(10)
}
.buttonStyle(PlainButtonStyle())
There isn't a need to add an overlay. You can substitute padding modifier with frame modifier. The action is a non return method outside of the body variable.
Right specifically for #MinonWeerasinghe:
Button(action: self.action) {
Text("Button Name")
.font(.system(size: 15))
.fontWeight(.bold)
.foregroundColor(.black)
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).stroke().foregroundColor(Color.red))
.cornerRadius(10)
}
.buttonStyle(PlainButtonStyle())
Just add the cornerRadius argument:
.border(Color.white, width: 2, cornerRadius: 25)
using this simple extension:
extension View {
func border(_ color: Color, width: CGFloat, cornerRadius: CGFloat) -> some View {
overlay(RoundedRectangle(cornerRadius: cornerRadius).stroke(color, lineWidth: width))
}
}
You can try this:
var body: some View {
ZStack {
Color.green
.edgesIgnoringSafeArea(.all)
HStack {
Button(action: {
print("sign up bin tapped")
}){
HStack {
Text("SIGN UP")
.font(.system(size: 18))
}
.frame(minWidth: 0, maxWidth: 300)
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 40)
.stroke(Color.white, lineWidth: 2)
)
}
}
}
}
I also did not set the maxWidth to .infinity because it means the button will fill the width of your container view.
The result will be :
Hope it helps :)
Swift version 5.6
You can use Button properties for example
Button(action: {
//define action
}) {
Image(systemName: "arrow.triangle.2.circlepath.circle.fill")
.imageScale(.large)
Text("Restart")
.font(.system(.title2))
}
.buttonStyle(.borderedProminent)
.buttonBorderShape(.capsule)
.controlSize(.large)
.buttonBorderShape(.roundedRectangle) //change bordershape see below
.buttonBorderShape(.roundedRectangle(radius: 4)) // see below
similarly you can change the buttonSytle and controlSize
This worked for me
Button(action: {
print("Exit the onboarding")
}) {
HStack (spacing: 8) {
Text("NEXT")
.foregroundColor(Color("ColorAccentOppBlack"))
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.foregroundColor(Color("ColorYellowButton"))
.background(
Capsule().strokeBorder(Color("ColorYellowButton"), lineWidth: 1.25)
)
}
.accentColor(Color("ColorYellowButton"))
You should use Capsule. This is built-in into SwiftUI. It takes care of rounded corners. Full implementation is here https://redflowerinc.com/how-to-implement-rounded-corners-for-buttons-in-swiftui/
public struct ButtonStyling : ButtonStyle {
public var type: ButtonType
public init(type: ButtonType = .light) {
self.type = type
}
public func makeBody(configuration: Configuration) -> some View {
configuration.label.foregroundColor(Color.white)
.padding(EdgeInsets(top: 12,
leading: 12,
bottom: 12,
trailing: 12))
.background(AnyView(Capsule().fill(Color.purple)))
.overlay(RoundedRectangle(cornerRadius: 0).stroke(Color.gray, lineWidth: 0))
}
}
🔴 To create a border with rounded corners, you can draw a rounded rectangle and overlay on the button like this:
Button(action: {
print("Hello button tapped!")
}) {
Text("Hello World")
.fontWeight(.bold)
.font(.title)
.foregroundColor(.purple)
.padding()
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.purple, lineWidth: 5)
)
}
Wonder how to add button border with color gradient and corner radius
Here's how..
Button(action: {self.isCreateAccountTapped = true},label: {Text("Create an Account")
.foregroundColor(Color("TextThemeColor36"))}
)
.frame(height: 44)
.frame(width: 166)
.background(Color.clear)
.cornerRadius(8)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(LinearGradient(gradient: Gradient(colors: [Color("BtnGradientClr1"),Color("BtnGradientClr2"),Color("BtnGradientClr3")]), startPoint: .leading, endPoint: .trailing)))
How can I increase the area in which a button can be triggered without changing the UI?
This is my code
struct ContentView: View {
var body: some View {
NavigationView {
Text("Text")
.navigationBarTitle(Text("Title"))
.navigationBarItems(
leading:
Button(action: { print("add") }) {
Image(systemName: SFSymbolName.plus)
.font(.system(size: 18))
}
)
}
}
}
For this particular situation, You can add padding to all edges excluding the leading edge to the label of the button:
Button(action: { print("add") }) {
Text("+")
.padding(EdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 50))
}
.background(Color.red) // This is just for seeing the hit area. You should get rid of it
Note that The maximum tappable area should be inside the rectangle above the title:
The Button is as big as it's content. In your case, it is as big as the image. Your button, or the tappable area, is small because the image is just small.
You can contain your image in a bigger frame, like this:
Button(action: { print("add") }) {
Image(systemName: SFSymbolName.plus)
.font(.system(size: 18))
.frame(width: 40, height: 40)
}