Consider having a screen like on the wireframe consisting of text, an image, and a sheet. When the sheet is dragged down, the image should grow vertically and go out of bounds.

This already works to a certain extent, but the image only scales vertically but not horizontally.

The code I used is the following:
struct SizePreferenceKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: Value = 0
static func reduce(value: inout Value, nextValue: () -> Value) {
value = nextValue()
}
}
struct ContentView: View {
@State private var selectedPresentationDetent: PresentationDetent = .fraction(0.6)
@State private var sheetPosition: CGFloat = 0
var body: some View {
GeometryReader { geometry in
VStack {
GeometryReader { geometryImage in
VStack(alignment: .center) {
Spacer()
Text("Hello, world!")
.padding()
AsyncImage(
url: URL(string: "https://images.unsplash.com/photo-1551709645-3f16f608bb80?ixlib"),
content: { image in
image.resizable()
.frame(height: geometryImage.size.height * 0.2 + sheetPosition)
.aspectRatio(contentMode: .fit)
},
placeholder: {}
)
.frame(height: geometryImage.size.height * 0.2 + sheetPosition)
}
}
.frame(height: geometry.size.height * 0.5 + geometry.safeAreaInsets.top)
}
.sheet(isPresented: .constant(true)) {
GeometryReader { sheetGeometry in
Color.blue
.ignoresSafeArea()
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled)
.presentationDetents([
.fraction(0.6),
.fraction(0.3)
], selection: $selectedPresentationDetent)
.presentationDragIndicator(.visible)
.preference(key: SizePreferenceKey.self, value: sheetGeometry.frame(in: .global).minY)
}
.onPreferenceChange(SizePreferenceKey.self) { preferences in
self.sheetPosition = preferences
}
}
}
}
}
Don't take this answer as production-ready, it needs improvements, though, this is my proposition:
CGSize
to expose a function to compute the aspect ratio, like this (handle the case ofself.height == 0
depending on what the expected behavior is in your app):AsyncImage
with the following code:This way you're manually making sure that the aspect ratio is preserved during drag.
AsyncImage
inside aZStack
with the following modifiers:Then you should have a close call to what you're looking for.