I have a view that is two halves of an Image, which I'm attempting to make a folding accordion style animation. As the viewed angle increases towards 90 degrees, the view should be increasingly closer to a height of 0.
struct HeroModuleView: View {
@Binding var viewedAngle: Double
@State private var offset = 0.0
@State private var imageFrame: CGRect?
var body: some View {
ZStack {
if let image = UIImage(named: "mountain-hero"),
let topHalf = image.topHalf,
let bottomHalf = image.bottomHalf {
VStack(spacing: 0) {
Image(uiImage: topHalf)
.resizable()
.scaledToFill()
.rotation3DEffect(.degrees(-viewedAngle), axis: (x: 1.0, y: 0.0, z: 0.0), anchor: .top)
.offset(y: offset)
.clipped()
.getFrame(frame: $imageFrame)
Image(uiImage: bottomHalf)
.resizable()
.scaledToFill()
.rotation3DEffect(.degrees(viewedAngle), axis: (x: 1.0, y: 0.0, z: 0.0), anchor: .bottom)
.offset(y: -offset)
.clipped()
}
.brightness(-(viewedAngle / 90) * 0.2)
}
}
.frame(maxHeight: 200)
.onReceive(Just(viewedAngle)) { angle in
self.offset = (angle / 90) * (imageFrame?.size.height ?? 0)
}
}
}
As you can see form the image, the frame, when the image is rotated in 3D space, begins to move towards the anchor point. I have tried adjusting things based on a .center
anchor, however the offset doesn't do anything I expect it to and frankly I can't work out the math behind it. I attempted to use some sin
calculations but those proved unfruitful. I need the frame of the ZStack
to clip
the height of both of these views after their effect has been applied, there should be no space above or below them.