1

I have some experience in SwiftUI, but am new to UIKit.

I'd like to import the zoom and position from one instance of an UIViewRepresentable UIKit ScrollView to another. So, basically, the user scrolls and zooms and later, in another branch of the view hierarchy, I want to start zoomed in at that zoom and position. I can't get it to work though, even after many attempts.

Below is my makeUIView function where I try to set the position and zoom that I want (after some initial setup).

   func makeUIView(context: Context) -> UIScrollView {
        // set up the UIScrollView
        let scrollView = UIScrollView()
        scrollView.delegate = context.coordinator
        scrollView.bouncesZoom = true
        scrollView.delaysContentTouches = false
        scrollView.maximumZoomScale = 0.85 * screenScale * 10
        scrollView.minimumZoomScale = 0.85 * screenScale
        
        // create a UIHostingController to hold our SwiftUI content
        let hostedView = context.coordinator.hostingController.view!
        hostedView.translatesAutoresizingMaskIntoConstraints = true
        hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        hostedView.frame = scrollView.bounds
        scrollView.addSubview(hostedView)
        
/*
 Here I add the zoom and position
*/
        scrollView.zoomScale = 0.85 * screenscale
        // add zoom and content offset
        if let zoomScale = zoomScale, let contentOffset = contentOffset {
            scrollView.contentOffset = contentOffset
            
            // make sure it is within the bounds
            var newZoomScale = zoomScale
            if zoomScale < scrollView.minimumZoomScale {
                print("too small")
                newZoomScale = scrollView.minimumZoomScale
            } else if zoomScale > scrollView.maximumZoomScale  {
                print("too large")
                newZoomScale = scrollView.maximumZoomScale
            }
            
            scrollView.setContentOffset(contentOffset, animated: true)
            scrollView.setZoomScale(newZoomScale, animated: true)
        }
        
        return scrollView
    }

The way I get the zoom and contentOffset in the first place is to grab the values from the Coordinator in first ScrollView instance using the below code. As far as I can tell this works well and I get updates with sensible values after zooming or scrolling. The first code snippet contains the makeCoordinator function where I initiate the coordinator with methods from an environmentObject (which then updates said object). The second snippet contains the Coordinator.

    func makeCoordinator() -> Coordinator {
        return Coordinator(hostingController: UIHostingController(rootView: self.content),
                           userScrolledAction: drawingModel.userScrollAction,
                           userZoomedAction: drawingModel.userZoomAction)
    }
    class Coordinator: NSObject, UIScrollViewDelegate {
        var hostingController: UIHostingController<Content>
        let userScrolledAction: (CGPoint) -> Void
        let userZoomedAction: (CGFloat) -> Void

        init(hostingController: UIHostingController<Content>, userScrolledAction: @escaping (CGPoint) -> Void, userZoomedAction: @escaping (CGFloat) -> Void) {
            self.hostingController = hostingController
            self.userScrolledAction = userScrolledAction
            self.userZoomedAction = userZoomedAction
        }

        func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return hostingController.view
        }

        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            userScrolledAction(scrollView.contentOffset)
        }

        func scrollViewDidZoom(_ scrollView: UIScrollView) {
            userZoomedAction(scrollView.zoomScale)
        }
    }
berrou
  • 11
  • 3
  • Use a shared object that will contains the scroll view offset and and zoom. You set values in first scroll view upon changes and in the other scroll view you can get these value from the shared object to init scroll view offset and zoom in viewWillAppear of the view controller handling the scroll view. – Ptit Xav May 16 '22 at 12:07
  • How do I access that controller? Is it the same controller as the UIHostingController that the Coordinator uses? – berrou May 16 '22 at 12:59
  • You can get the end of scrolling and dragging in scrollview delegate and save the value in share object. As you are using a SwiftUI view to include your second scroll view you could use the onAppear of the SwiftUI view containing the second scroll view to get the value from shared object and pass it to second scroll view as state var for example. – Ptit Xav May 16 '22 at 16:27
  • Can you show how you use the coordinators and the coordinators ? This could help find as solution. – Ptit Xav May 17 '22 at 06:41
  • I already get the scrolling and dragging from the coordinator (I'll put more details on that in the post in a minute). I also manage to pass the values to the UIViewRepresentable. But I don't get how to access the UIKit controller since it is an UIViewRepresentable I've implemented. And when I set the contentOffset and zoomScale in the makeUIView (or in the updateUIView) I get weird results. – berrou May 17 '22 at 06:51
  • What are screenscale and fullpagezoomacale. Did you try setting constants values to see if that wok better ? – Ptit Xav May 17 '22 at 08:42
  • Screenscale is a constant defined elsewhere and fullpagezoomscale is a variable that is equal to 0.85 * screenScale. I removed the latter before posting to make the code shorter, but unfortunately I forgot to replace all occurrences. Will update the post! – berrou May 17 '22 at 09:00

0 Answers0