How to place texts under an image in SwiftUI? - swift

I'm trying to create some tiles in my swiftUI project.
The tiles will need to look like this:
Basically, I need to place the caption on the left and the texts under the image.
I can create the image and I can overlay the texts on the image but I cannot place them under the image and I cannot place the caption to the left.
This is my current code:
Image("flower")
.resizable()
//.aspectRatio(contentMode: .fill)
.frame(width: 160, height: 200)
// Overlay is a very simple way to add content on top of your view ! 😁
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.shadow(color: Color.black.opacity(0.3), radius: 6, x: 0, y: 3)
VStack(alignment: .trailing, spacing: 6) {
Text("sit amet")
.background(Color.gray.opacity(0.2))
.font(.caption)
}.onTapGesture {
// Handle the action when the card is touched
}
can someone please advice on this?

Put the Image and text both under VStack as follows:
VStack(alignment: .leading, spacing: 6) {
Image("flower")
.resizable()
//.aspectRatio(contentMode: .fill)
.frame(width: 160, height: 200)
// Overlay is a very simple way to add content on top of your view ! 😁
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.shadow(color: Color.black.opacity(0.3), radius: 6, x: 0, y: 3)
Text("sit amet")
.background(Color.gray.opacity(0.2))
.font(.caption)
}.onTapGesture {
// Handle the action when the card is touched
}
Text("Some Info goes here")
}

Related

Format number inside image bubble

I'm having some trouble understanding how to properly align items in top of each other:
So I have the following VStack:
VStack(spacing: 0) {
Text("\(placeC.places.count)")
Image("map-pin-full-cluster-1")
.renderingMode(.template)
.resizable()
.frame(width: 35, height: 35)
.foregroundColor(.brown)
} //: END VStack
Which produces the following output:
What is the best way to have the number inside the circle? Is there a recommended SwiftUI builder that you all use that I can explore?
Typically you would use an .overlay:
Image("map-pin-full-cluster-1")
.renderingMode(.template)
.resizable()
.frame(width: 35, height: 35)
.foregroundColor(.brown)
.overlay(alignment: .top) {
Text("\(placeC.places.count)")
}
or a ZStack:
ZStack(alignment: .top) { // other alignments: .center, .bottom, .leading, .trailing
Image("map-pin-full-cluster-1")
.renderingMode(.template)
.resizable()
.frame(width: 35, height: 35)
.foregroundColor(.brown)
Text("\(placeC.places.count)")
.padding(.top, 4) // adjust this to move the text up/down
}

SwiftUI Horizontal ScrollView onTapGesture is Off

I am experiencing some very odd behavior from a Horizontal ScrollView, it seems as if the clickable frame of the cells are offset. For example, if you click on left half of the first cell it will register the click for the first cell, but if you click the right half of the first cell it will register a click on the second cell. I'm not sure why this is happening.
I've included the code below, you can copy and paste this code into a playground to reproduce the issue. Any ideas here?
struct TestView: View {
var image: String
var body: some View {
ZStack {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 104, height: 168)
.cornerRadius(8)
.clipped()
VStack {
Spacer()
Text("Testing")
.foregroundColor(Color.white)
.frame(alignment: .leading)
.padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
.shadow(radius: 5)
Text("1:40")
.foregroundColor(Color.white)
.frame(alignment: .leading)
.padding(EdgeInsets(top: 3, leading: 10, bottom: 10, trailing: 10))
.shadow(radius: 5)
}
.frame(width: 104, height: 168)
}
}
}
struct TestViewCollection: View {
var collection = ["tv", "faxmachine", "printer"]
var body: some View {
ScrollView {
HStack {
ForEach(collection, id: \.self) { image in
TestView(image: image)
.onTapGesture {
print(image)
}
}
}
}
}
}
PlaygroundPage.current.setLiveView(TestViewCollection())
The issue is that your images are not the same aspect ratio as the frame you have put them in, so they are overflowing the frame. The solution is pretty simple. Instead of using .clipped() which just clips what you can see, use .contentShape() which will will provide an area that the .tapGesture() will use as its tappable area. Like this:
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 104, height: 168)
.cornerRadius(8)
.contentShape(Rectangle())
the problem is:
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 104, height: 168)
.cornerRadius(8)
.clipped()
Change that .aspectRatio(contentMode: .fill). -> into .fit
if you just move .clipped() like this:
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fill)
.clipped()
.frame(width: 104, height: 168)
You are going to see:
That's why is happening.

How to add a Circle() behind a png image in Swift

I have a google image stored that have transparent background. Because the blue part of the logo is almost the same as the background (as the 1st image) where is at, I want to add a white circle behind it.
To do it, this is what I did:
ZStack{
Circle().frame(width: 40, height: 40)
.background(Color.white)
Image("Google")
.renderingMode(.original)
.resizable()
.frame(width: 35, height: 35)
.padding(.all, 3)
}
But, what I'm getting is this, as if the center of the image doesn't " recognizes" there's a white circle (its also displaying a square instead of a circle) in front of the blue background :
How can I solve this problem?
You can use clipShape to make it a circle like this:
ZStack{
Circle().frame(width: 40, height: 40)
.background(Color.white)
.clipShape(Circle())
Image("Google")
.renderingMode(.original)
.resizable()
.frame(width: 35, height: 35)
.padding(.all, 3)
}
If you want to get ride of the circle background and keep just it's line, you can use .stroke() as shown below:
ZStack{
Circle()
.stroke()
.frame(width: 40, height: 40)
.background(Color.white)
.clipShape(Circle())
Image("Google")
.renderingMode(.original)
.resizable()
.frame(width: 35, height: 35)
.padding(.all, 3)
}
After reading the answers given, I got an easier way to solve it. I just changed .background(Color.white) to .foregroundcolor(.white)

Swift UI: Center Text into Circle

I try to put a circle-shaped frame around a text view, but I just can't get it properly aligned and I can't see where the problem is. As you can see in the picture below, the text is sometimes offset to left, while it should be centred. Any ideas on how to fix it?
struct ContentView: View {
let result = getDate();
var body: some View {
VStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 5) {
ForEach(result, id: \.self) {
day in
Text(day.name!)
.frame(width: 35, height: 35, alignment: .center)
.padding()
.overlay(
Circle()
.size(width: 35, height: 35)
.offset(x: 17.5,y: 17.5)
.scale(1.4)
.stroke(Color.orange, lineWidth: 4)
)
}
}
}
Spacer()
}.background(Color.white)
}
}
The circle is offset by half of the size of the frame, so its origin should be in the centre. The Text should be centered as well in .frame(width: 35, height: 35, alignment: .center).
Thanks a lot for helping! :)
.overlay already has size of container with Text an Circle centred in it, so it is just needed to operate with insets of circle not shifting it, like below
Text(day.name!)
.frame(width: 35, height: 35, alignment: .center)
.padding()
.overlay(
Circle()
.stroke(Color.orange, lineWidth: 4)
.padding(6)
)

Gesture blocking buttons in ZStack

I have a gesture added to a Rectangle inside a ZStack. I have a VStack which is a view or menu of buttons. When I add the gesture to the Rectangle (because I need to detect a tap on that view behind the menu buttons), then the buttons can not receive interaction from the user on device. I have the Rectangle behind the buttons and they do show above the Rectangle. On the simulator it worked just fine but on device it does not. Any help would be greatly appreciated from the community. Thanks!
var body: some View {
ZStack {
Rectangle()
.foregroundColor(Color.black.opacity(0.01))
.gesture(DragGesture(minimumDistance: 0)
.onChanged({ (value) in
self.store.tapped = true
}))
VStack(alignment: .leading, spacing: 0) {
//menu buttons
HStack(alignment: .center, spacing: 18.5) {
someView()
.environmentObject(self.anotherStore)
.frame(width: 145, height: 22)
self.otherSubviews()
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 37, alignment: .leading)
.padding(EdgeInsets(top: 0, leading: 6, bottom: 0, trailing: 6))
//menu detail view
self.detailView()
}
.padding(EdgeInsets(top: 6, leading: 6, bottom: 6, trailing: 12))
}
}
I have tried changing the .zIndex for both to -1 for the Rectangle to something higher for the menu view. It does not change anything
I would like for the Rectangle to receive interaction (taps) from the user and also that the VStack of buttons will also receive interaction from the user.
Figured out that with a view in the background that has a gesture you need to make sure that your other views have .contentShape(Specify A Shape).
In the Apple documentation it states
/// Returns a new view that defines the content shape for
/// hit-testing `self` as `shape`.
I added .contentShape(Rectangle()) to the VStack and it allowed taps for the menu view of buttons as well as allowing taps to the Rectangle view in the background.
Updated code with answer
var body: some View {
ZStack {
Rectangle()
.foregroundColor(Color.black.opacity(0.01))
.gesture(DragGesture(minimumDistance: 0)
.onChanged({ (value) in
self.store.tapped = true
}))
VStack(alignment: .leading, spacing: 0) {
//menu buttons
HStack(alignment: .center, spacing: 18.5) {
someView()
.environmentObject(self.anotherStore)
.frame(width: 145, height: 22)
self.otherSubviews()
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 37, alignment: .leading)
.padding(EdgeInsets(top: 0, leading: 6, bottom: 0, trailing: 6))
//menu detail view
self.detailView()
}.contentShape(Rectangle()) //Define a tappable area
.padding(EdgeInsets(top: 6, leading: 6, bottom: 6, trailing: 12))
}
}