Reputation: 35
I am working on a SwiftUI view that displays an image fetched from a URL using AsyncImage. I want the image to fill the entire screen, and I have a row of buttons overlaid at the bottom of the image within a ZStack. However, when I set the AsyncImage's contentMode to .fill, the buttons are not visible. Changing the contentMode to .fit makes the buttons appear, but the image no longer fills the entire screen as desired.
Here is the code snippet demonstrating the issue:
import SwiftUI
struct ImageViewTest: View {
private let wallpaperPath = "https://w.wallhaven.cc/full/7p/wallhaven-7pmj9o.jpg"
var body: some View {
ZStack(alignment: .center) {
AsyncImage(url: URL(string: wallpaperPath)) { phase in
switch phase {
case .empty:
ProgressView()
case .failure:
Text("Failed to fetch image")
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fill) // Changing this to .fit shows the buttons
.clipped()
@unknown default:
fatalError()
}
}
.ignoresSafeArea(.all)
VStack {
Spacer()
HStack {
Button(action: {}) {
Image(systemName: "square.and.arrow.down")
.foregroundStyle(.blue)
}
Spacer()
Button(action: {}) {
Image(systemName: "heart")
.foregroundStyle(.blue)
}
Spacer()
Button(action: {}) {
Image(systemName: "arrow.down.circle")
.foregroundStyle(.blue)
}
}
.padding()
.background(.ultraThinMaterial)
.cornerRadius(10)
}
}
}
}
#Preview {
ImageViewTest()
}
My goal is to have the image fill the entire screen while keeping the buttons visible at the bottom. I would appreciate any suggestions or insights into what might be causing the buttons to be covered when using .fill for the contentMode.
Upvotes: 3
Views: 812
Reputation: 119302
That's because the image caused the ZStack
's width
to overflow the screen and you need to prevent this somehow.
If you are targeting iOS 17 or later, adding the following modifier to the AsyncImage
would be enough:
.containerRelativeFrame(.horizontal)
One way is to use background
modifier on the VStack
instead of using ZStack
like:
VStack { ,,, }
.background {
AsyncImage(url: URL(string: wallpaperPath)) { phase in ,,, }
,,,
}
Both of the above methods would cause the view appear like this:
Upvotes: 1
Reputation: 271410
The reason this happens is that the image has a large width, making the ZStack
, and by extension the HStack
at the bottom, horizontally too. This makes the two buttons on the sides move outside of the bounds of the screen.
To fix this, you should limit the image's size to the screen size. You can do this by reading the available space using a GeometryReader
:
GeometryReader { geo in
image
.resizable()
.aspectRatio(contentMode: .fill)
// If the image can also be very large vertically,
// it will push the `HStack` downwards.
// so I recommend constraining the heigh as well
.frame(maxWidth: geo.size.width, maxHeight: geo.size.height)
}
Alternatively, add .containerRelativeFrame([.horizontal, .vertical])
on the AsyncImage
on iOS 17+.
IMO, it still isn't very visible because the contrast is too low. I recommend changing the tint colors of the buttons.
Upvotes: 1