I originally posted a question here on how to begin to dismiss or close a view, that contains a ScrollView, when the scroll reaches the top, in a similar way that Apples App store cards close, or the way Instagram dismisses a view on a down gesture. But using SwiftUI
Obviously if you just add a Draggesture and use it to offset or scale the view, it wouldn't get read because the scrollview would read first and block the drag gesture from being recognized.
I didn't get an answer here but fortunately I figured it out, and rather than post an answer I just updated my code. I created a UIScrollView using UIViewRepresentable and added some lines of code to add a PanGestureRecognizer.
struct ContentView: View {
@ObservedObject var vm = ViewModel.vm
@State var scale: CGFloat = 1
var body: some View {
CustomScrollView {
ZStack (alignment: .center){
VStack (spacing:0){
Image("Image")
.resizable()
.scaledToFit()
Text("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.")
.font(.system(size: 24))
.padding(.horizontal)
} //V
.ignoresSafeArea()
} //Z
} //SCROLLVIEW
.frame(width: Screen.width, height: Screen.height, alignment: .center)
.ignoresSafeArea()
.scaleEffect(self.scale)
.onChange(of: vm.y) { y in
if y < 101 { //Set point at which to begin scaling down
withAnimation(.linear(duration: 0.15)) {
self.scale = (1-(y/500)) //Calculates scaling using translation.y from the Gesture Recognizer
}
} //IF
if y == 100 {withAnimation(.default) { //Limits the drag based on translation.y
self.scale = 1
}
}//IF
} //ONCHANGE
}
}
struct CustomScrollView<Content:View> : UIViewRepresentable {
@ObservedObject var viewValues = ViewModel.vm
typealias UIViewType = UIScrollView
let content: Content
init (
@ViewBuilder content: () -> Content
) {
self.content = content()
}
// MARK: - MAKEUIVIEW
func makeUIView(context: Context) -> UIScrollView { //Makes the initial ScrollView
let scrollView = UIScrollView()
// Child
let child = UIHostingController(rootView: content)
scrollView.addSubview(child.view)
// Child Size
let newSize = child.view.sizeThatFits(CGSize(width: Screen.width, height: .greatestFiniteMagnitude))
child.view.frame = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
scrollView.contentSize = newSize
//Adds Pan gesture Recognizer
scrollView.delegate = context.coordinator
scrollView.panGestureRecognizer.addTarget(context.coordinator, action: #selector(Coordinator.dragged))
return scrollView
}
//Update ScrollView
func updateUIView(_ scrollView: UIScrollView, context: Context) {
}
// MARK: - COORDINATOR
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject, UIScrollViewDelegate, ObservableObject {
var offset: CGFloat = 0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.offset = scrollView.contentOffset.y
if scrollView.contentOffset.y < 1 { //Turns Bounce off at top, and sets Offset to Zero.
scrollView.contentOffset.y = 0
scrollView.bounces = false
} else { scrollView.bounces = true } //IF
} //ViewDiDScroll
@objc func dragged(gestureRecognizer: UIPanGestureRecognizer) { // Pan Gesture Recognizer
var translation = gestureRecognizer.translation(in: gestureRecognizer.view)
let state = gestureRecognizer.state
if translation.y > 100 {translation.y = 100} //Sets a drag limit at which the view will close
if offset == 0 { ViewModel.vm.y = translation.y } //Set a point at which the view should start closing
if state == .ended { ViewModel.vm.y = 0 } //Reset Y value
} //@objc
} //COORDINATOR
} //END
class ViewModel: ObservableObject {
static var vm = ViewModel()
@Published var y : CGFloat = 1 // Scale View Down
}
struct Screen {
static let width = UIScreen.main.bounds.width
static let height = UIScreen.main.bounds.height
}