I would like to clip a view (image) into a circle as the view is dragged down to be dismissed (as in the video below). My current approach has some bugs because dragging the view while changing the frame is very buggy. What is the best way to reproduce the clip/transition in the video?
https://drive.google.com/file/d/1CUzGdsbO4Nx4qQgdonVWMzA_Na0VmwCS/view?usp=sharing
import SwiftUI
struct uyjrbertbr: View {
@State var offset: CGSize = .zero
@State var cornerRad: CGFloat = 0.0
@State var widthStory = 0.0
@State var heightStory = 0.0
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: cornerRad)
.foregroundStyle(.blue)
.ignoresSafeArea()
.frame(width: widthStory == 0.0 ? widthOrHeight(width: true) : widthStory)
.frame(height: heightStory == 0.0 ? widthOrHeight(width: false) : heightStory)
.offset(x: offset.width, y: offset.height)
.gesture(DragGesture()
.onChanged({ value in
if value.translation.height > 0 {
self.offset = value.translation
clip()
}
})
.onEnded({ value in
withAnimation(.easeInOut(duration: 0.2)) {
offset = .zero
cornerRad = 0.0
widthStory = widthOrHeight(width: true)
heightStory = widthOrHeight(width: false)
}
})
)
}
.onAppear(perform: {
widthStory = widthOrHeight(width: true)
heightStory = widthOrHeight(width: false)
})
}
func clip() {
var ratio = abs(self.offset.height) / 200.0
ratio = min(1.0, ratio)
self.cornerRad = ratio * 300.0
let min = 80.0
let maxWidth = widthOrHeight(width: true)
let maxHeight = widthOrHeight(width: false)
widthStory = maxWidth - ((maxWidth - min) * ratio)
heightStory = maxHeight - ((maxHeight - min) * ratio)
}
}
#Preview {
uyjrbertbr()
}
func widthOrHeight(width: Bool) -> CGFloat {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
if width {
return window?.screen.bounds.width ?? 0
} else {
return window?.screen.bounds.height ?? 0
}
}