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.
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: