'In SwiftUI what is an easy way to align more than one view in ZStack
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?
Solution 1:[1]
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()
)
Solution 2:[2]
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)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |