In SwiftUI what is an easy way to align more than one view in ZStack - swift

I have a view and I want to add two icons to it, at top right side and at bottom right side. I managed to do that:
I used two ZStacks:
ZStack(alignment: .bottomTrailing)
{
ZStack(alignment: .topTrailing)
{
Image(item.thumbnailImage)
.clipShape(Circle())
.overlay(Circle()
.stroke(Color.gray, lineWidth: 2))
if item.isFavorite
{
Image(systemName: "star.fill")
.foregroundColor(.yellow)
.offset(x: 7, y: -7)
}
}
if item.ordered
{
Image(systemName: "checkmark.square.fill")
.offset(x: 7, y: 7)
}
}
But I have a feeling that there should be a simpler way than nesting ZStacks inside. Besides looks like the small icons don't have their x-centers aligned. I can probably fix that by changing an offset but that would make the code even more clumsy.
Is there a simpler way?

Use only one Zstack and wrap the two icons in a VStack.
ZStack(alignment: .trailing) {
Circle()
.stroke(Color.gray, lineWidth: 2)
VStack {
Image(systemName: "star.fill")
Spacer()
Image(systemName: "checkmark.square.fill")
}
// Adjust the position of star and checkmark
.offset(x: -10)
}
.frame(width: 100, height: 100)

You can use the overlay modifier, like this:
import PlaygroundSupport
import SwiftUI
PlaygroundPage.current.setLiveView(
Circle()
.strokeBorder(Color.gray, lineWidth: 6)
.frame(width: 44, height: 44)
.overlay(
Image(systemName: "star.fill")
.foregroundColor(.yellow)
.offset(x: 7, y: -7),
alignment: .topTrailing)
.overlay(
Image(systemName: "checkmark.square.fill")
.offset(x: 7, y: 7),
alignment: .bottomTrailing)
.padding()
)
If your deployment target is iOS 15 or later (or an aligned version of macOS, tvOS, or watchOS), you can use the ViewBuilder version of overlay instead:
import PlaygroundSupport
import SwiftUI
PlaygroundPage.current.setLiveView(
Circle()
.strokeBorder(Color.gray, lineWidth: 6)
.frame(width: 44, height: 44)
.overlay(alignment: .topTrailing) {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
.offset(x: 7, y: -7)
}
.overlay(alignment: .bottomTrailing) {
Image(systemName: "checkmark.square.fill")
.offset(x: 7, y: 7)
}
.padding()
)
We can use lorem's suggestion to align the symbol centers using a VStack. Then we can factor out the two offset modifiers into a padding on the VStack.
import PlaygroundSupport
import SwiftUI
PlaygroundPage.current.setLiveView(
Circle()
.strokeBorder(Color.gray, lineWidth: 6)
.frame(width: 44, height: 44)
.overlay(
VStack(spacing: 0) {
Image(systemName: "star.fill")
.foregroundColor(.yellow)
Spacer()
Image(systemName: "checkmark.square.fill")
}.padding([.top, .bottom, .trailing], -7),
alignment: .trailing)
.padding()
)

Related

Why does ScrollView randomly push my views content downwards?

Here is my code:
import SwiftUI
struct TrendingView : View {
#State var selectedTab: Tabs = .hot
#State private var searchText = ""
var body: some View {
NavigationView {
VStack {
HStack {
Text(" Trending")
.font(.largeTitle)
.frame(width: 175, height: 50, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.italic()
.fontWeight(.heavy)
.foregroundColor(Color(hue: 1.0, saturation: 0.977, brightness: 0.985))
.offset(x: -75, y: -25)
.padding(.bottom, -20)
NavigationLink(destination: testT()) {
Image(systemName: "magnifyingglass.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.foregroundColor(.black)
.padding(.bottom, -20)
}
.offset(x: 50, y: -25)
.padding(.leading, 5.0)
}
ScrollView {
VStack {
Text("Hot Items 🔥")
.bold()
.italic()
.offset(x: 5)
.frame(width: 120, height: 25, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.background(.yellow)
.offset(x: -130, y: 5)
ScrollView(.horizontal) {
HStack {
NavigationLink(destination: ImageTest()) {
Image("testpic1")
.resizable()
.frame(width: 200, height: 200)
}
NavigationLink(destination: ImageTest()) {
Image("testpic3")
.resizable()
.frame(width: 200, height: 200)
}
}.offset(y: 30)
}
}
}
There seems to be some error with scrollview above my vstack as it randomly causes all the content on this view to shift downwards when switching to and from other views.Ive tried removing the scrollview and the error disappears and also tried changing it around in other places but I still get the error Can anybody point me in the right direction? Thanks
Fixed, added to the bottom of my code and it seems like the issue is solved
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)

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.

SwiftUI How to align the bottom of two text with different font size?

I’m trying to align the bottom of two Text but the larger one looks like thismy widget looks like this. I don’t know why there’s still some space below this Text view.
This is my code:
var body: some View {
GeometryReader{geometry in
ZStack(alignment: .center){
Image("background")
.resizable()
VStack(){
HStack(alignment: .bottom){
Text("3")
.lineLimit(1)
.font(.bold(.system(size: 100))())
.minimumScaleFactor(0.1)
.foregroundColor(Color.white)
.frame(width: nil, height: geometry.size.height/3, alignment: .bottom)
.lineLimit(1)
.shadow(radius: 2)
Text("cups")
.foregroundColor(Color.white)
.font(.system(size: 12))
.shadow(radius: 2)
}
Text("Next cup on")
.foregroundColor(Color.white)
.font(.system(size: 10))
.shadow(radius: 2)
.opacity(0.5)
Text("10 : 00")
.foregroundColor(Color.white)
.font(.system(size: 12))
.shadow(radius: 1)
.opacity(0.5)
}
}
}
}
What I want is the number 3’s bottom aligned to “cups”’s bottom.
Thanks!
Instead of .bottom, you can use .lastTextBaseline as your HStack alignment:
HStack(alignment: .lastTextBaseline) {
Text("Hello").font(.system(size: 40))
Text("World").font(.system(size: 10))
}

SwiftUI layers in HStack

When I drag the rectangle to the left, the symbol is under the rectangle but on the right the symbol is over the rectangle, when I drag it to the right. I know, that the hierarchy of the HStack decides the layer but if I would do the right symbol over the rectangle, then both of the symbols are on the right side. I'm new at SwiftUI
HStack(spacing: 0.0){
Image(systemName: "lessthan.square")
.resizable()
.frame(width: 20.0, height: 20.0)
.padding(.leading, 30.0)
ZStack(alignment: .center) {
Group {
Rectangle()
.cornerRadius(20.0)
.frame(width: 250, height: 150)
.foregroundColor(Color(.white))
.position(rectPosition)
.shadow(radius: 5)
VStack{
Text("i")
.font(.system(size: 30))
Text("l")
.font(.system(size: 25))
}.position(rectPosition)
}.gesture(DragGesture().onChanged({ value in
self.rectPosition = CGPoint(x: value.location.x, y: 130)
}))
}
Image(systemName: "greaterthan.square")
.resizable()
.frame(width: 20.0, height: 20.0)
.padding(.trailing, 30.0)```
zIndex should help you... add it to ZStack
HStack {
Image(...)
ZStack {
...
}.zIndex(1) // << here !!
Image(...)
}