Button Corner to round button (Xcode,Swift) [duplicate] - swift

With UIKit, it's possible to give a control (e.g. a button) perfectly rounded corners (resulting in a circle on each side) by using this approach:
exampleButton.cornerRadius = exampleButton.frame.size.height/2
How does one achieve the same result with a SwiftUI view?
Because the views are defined more on the fly, I'm not sure how it would be possible to reference that frame size unless it's being set manually (which isn't the desire here).
Button(action: {
// ...
}) {
Text("I'm a Button")
}
.cornerRadius(???)

Another solution to this is to use shapes, in this case the Capsule shape, and use the clipShape modifier
Taking the example already mentioned, it would be like this:
Button(action: {
// ...
}) {
Text("I'm a Button")
.padding(.horizontal, 10)
.background(Color.red)
.clipShape(Capsule())
}
The padding in there can be adjusted so that your view looks how you want it to. The point it that capsule will always have the ends perfectly rounded. In this case I didn't want the text to be too close to the rounded edges, so I applied some padding to the sides.
A note to remember is that in SwiftUI the order the modifiers are applied in is very important.

You need to define it as a square and then put rounding on corners, like this:
Button(action: {
// ...
}) {
Text("I'm a Button")
.frame(width:150, height:150)
.background(Color.red)
.cornerRadius(.infinity)
}
PS. Added some background color for visibility

Working in XCode 12.4
For anyone who found this question trying to find out how to make a button with rounded corners (whether completely round like a capsule or not):
Button(action: {
// Do whatever
}) {
Spacer()
Text("Log In")
.font(.title2)
.padding()
.foregroundColor(.white)
Spacer()
}
.background(Color(UIColor.systemBlue))
.clipShape(RoundedRectangle(cornerRadius: 12))
.padding()
No need to fuss around with overlay shapes.

Another solution is to use geometry reader which will take the height or width of the screen and allow you to divide, multiply subtract or add from it.
Using the example, it would be like this:
GeometryReader { Geometry in
Button(action: {
// ...
}) {
Text("I'm a Button")
}
.cornerRadius(geometry.size.width / 2)
}
It is the most similar to frame.size.height.

Related

Remove spacing between HStacks embedded in Stack

I am trying to stack two HStacks filled with circles, however, no matter what I do, I cannot get rid of the spacing between the two.
VStack {
VStack(spacing: 0) {
HStack {
ForEach(viewModel.lights) { light in
Circle()
.foregroundColor(light.color)
}
}
HStack {
ForEach(viewModel.lights) { light in
Circle()
.foregroundColor(light.color)
}
}
} .padding()
Button(action: viewModel.start ) {
Text("Start")
}
}
Since the Circles have no height constraint, they are taking up all of their available vertical space, even though the visible shape doesn't take up that space. Horizontally, they're limited by the width of the device/screen.
You can add .aspectRatio(contentMode: .fit) to make them constrained vertically as well to only the space they need to take up:
Circle()
.aspectRatio(contentMode: .fit)
.foregroundColor(light.color)
Once that is done, if you also want to push them towards the top of the screen, you can add a Spacer below the HStacks.

Round Specific Corners of Stroke/Border SwiftUI

I am working on a SwiftUI project where I need a view to have a border with only some of the corners rounded (for instance, the top left and top right).
I added a RoundedRectangle with a stroke and was able to have all of the corners rounded. However, I need only some of the corners to be rounded and I couldn't figure out a way to do that.
This is the code I had to add a RoundedRectangle:
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(Color.gray, lineWidth: 1)
)
To make only specific corners rounded, I looked at this Stackoverflow post: Round Specific Corners SwiftUI. However, I would have to get rid of the Rounded Rectangle (because it rounds all corners). I would have to use a normal border instead. But, with a normal border, it will cut out a piece of the border when rounding corners and trying any of the answers provided.
This is what I would ideally want it to look like (this is from an example from Bootstrap - we are rebuilding a website as an app):
Thank you!
Here's an alternative / easier way to recreate this. Add a white background to each of the rows and add a single gray background color behind them. Add spacing between the rows to let the gray background color appear like a divider between them. Then just add a rectangle overlay to the entire view, like you already had in your code!
struct CornerView: View {
var body: some View {
VStack(spacing: 1) {
ForEach(0..<5) { index in
Text("Item \(index)")
.frame(maxWidth: .infinity)
.frame(height: 55)
.background(Color.white)
}
}
.background(Color.gray)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 1)
)
.padding()
}
}
struct CornerView_Previews: PreviewProvider {
static var previews: some View {
CornerView2()
}
}

HStack inside ScrollView animates weirdly when opening view

So I was trying to implement expanding/contracting rows in a for each embedded in a list view and it turns out the list view cells don't animate smoothly. After checking out this tutorial, it suggests using a scrollview with a foreach because it animates smoother. The expanding/contracting works fine, but there unintended side effects only when I first open the page. The HStack appears to start flattened on the left side of the view and animates expanding to its normal starting position. I've slowed down the animations in the video so it's easier to see, and I removed the ForEach since it doesn't seem to be the cause of the problems. I don't know what is causing this and google searching has yielded no answers. Does anyone here have an answer or at least some advice? Much appreciated
#State var showTemp: Bool = false
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
HStack(alignment: .top) {
Text("Hello")
.font(.system(size: 32))
if showTemp {
VStack {
Text("Middle 1")
Text("Middle 2")
Text("Middle 3")
Text("Middle 4")
Text("Middle 5")
}
}
Spacer()
Text("Goodbye")
}
.border(Color.black, width: 2)
.onTapGesture {
self.showTemp.toggle()
}
.background(Color.blue)
.animation(.default)
}
.background(Color.green)
.onAppear(perform: loadMethod)
}
It seems I've answered my own question after I looked at the problem from a different angle. Using GeogetryReader and setting the frame width of the HStack to geometry.size.width fixed it.
I think this works because without specifying the width, the HStack fills as much area as it can. For some reason, it starts smushed when the view is loaded and then expands to fill its area, which I guess isn't visible unless you apply an animation to it.
Trying to be helpful here, I don't want to be one of those people who answers their own questions but doesn't show what the answer it.

SwiftUI changing the color of clear part inside of SF Symbol

I am trying to change the color of clear(transparent) part inside of a SF Symbol called delete.left.fill. So far I've tried is as follows
Button(action: { return }, label: {
Image(systemName: "delete.left.fill")
.background(Color.black)
//.accentColor(.black)
.font(.system(size: self.fontSize*0.75))
})
.frame(width: self.width, height: self.width)
.foregroundColor(.lightGray)
//.background(Color.black)
When I run the code as above, the result is like
.
At first, the xinside of the symbol was the same color as background. I want it to make black.
I tried to set the backgroundColor of the Button and it made
whole Button black.
I tried to set accentColor of the Image to
black. Nothing changed.
I tried to set backgroundColor of the
Image to black. The result can be seen in the image.
The question is, is there a way to make just that x, inside the symbol, black programmatically?
You could mask the background and apply a custom style:
struct MyButtonStyle: ButtonStyle {
public func makeBody(configuration: MyButtonStyle.Configuration) -> some View {
configuration.label
.compositingGroup()
.opacity(configuration.isPressed ? 0.5 : 1.0)
}
}
Button(action: {}) {
Image(systemName: "delete.left.fill")
.foregroundColor(.green)
.background(
Color.black.mask(Circle())
)
}.buttonStyle(MyButtonStyle())
The circle may not fit to any usage, but for this image it works okay:
As he warned with the circle, #Faruk's solution didn't work for me with the exclamationmark.triangle.fill
I created a ZStack with the fill and unfill versions to create a yellow triangle with a black exclamation point and a black border:
ZStack{
Image(systemName: "exclamationmark.triangle")
.foregroundColor(Color.black)
.scaleEffect(1.1)
Image(systemName: "exclamationmark.triangle.fill")
.foregroundColor(Color.yellow)
}
I've only tried on couple of simulators, but it looks nice on iPadPro and iPad Mini
as of iOS 15 you can simply achieve that with .foregroundStyle(.red, .blue)

SwiftUI: Increase tap/drag area for user interaction

I've built a custom slider in SwiftUI with a thumb dragger that is 20x20. Everything is working, except the tap target is quite small since it's just the 20x20 view. I'm looking for a way to increase this to make it easier for users to interact with my slider. Is there a way to do this in SwiftUI?
I tried wrapping my thumb in Color.clear.overlay and setting the frame to 60x60. This works if the color is solid, like red, but with clear the tap target seems to revert back to visible pixels of 20x20.
You can see on this gif I'm able to drag the slider even when clicking outside of the thumb.
However, as soon as I change the color to clear, this area no longer receives interactions.
Add a .contentShape(Rectangle()) after the frame.
I'm sure there are several ways to accomplish this, but this is how i made a big button with the whole area tappable:
Button(action: { someAction() }, label: {
Text("OK")
.frame(minWidth: 200)
.contentShape(Rectangle())
.padding()
})
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(5.0)
I had the same problem. Except I didn't want the expanded overlay to impact the rest of the layout. If you embed everything in a ZStack and put a rectangle before your interactive object, you can control the size of the gesture. Kind of like this:
Rectangle().frame(width: screen.width, height: 300).opacity(0.001)
.layoutPriority(-1)
Just needed to make sure to set the opacity to next to nothing so you can't see it, and the layout priority to -1 so it doesn't impact the view.
As a follow up to the answer above, You can enlarge the clickable rect all day using this as example.
ZStack {
Image(systemName: secured ? "eye.slash" : "eye")
.resizable()
.renderingMode(.template)
.aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16)
Rectangle()
.frame(width: 22, height: 20)
.opacity(0.001)
.onTapGesture {
self.secured.toggle()
}
}