如何在 swiftui 中使用 UIscrollview 缩小过去的视图边界

How to zoom out past view bounds with UIscrollview in swiftui

我正在尝试制作一个 UIscrollview,它可以缩小到它承载的视图的原始框架之外。这是我的代码:

import SwiftUI
import UIKit


struct MapWrapperView<Content: View>: UIViewRepresentable {
    private var content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    func makeUIView(context: Context) -> UIScrollView {
        let scrollView = UIScrollView()
        scrollView.delegate = context.coordinator
        scrollView.minimumZoomScale = 0
        scrollView.maximumZoomScale = 100
        scrollView.bouncesZoom = false
        scrollView.bounces = false
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.clipsToBounds = false
        
        let hostedView = context.coordinator.hostingController.view!
        hostedView.translatesAutoresizingMaskIntoConstraints = true
        hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        hostedView.frame = scrollView.bounds
        scrollView.addSubview(hostedView)
        
        return scrollView
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(hostingController: UIHostingController(rootView: self.content))
    }
    
    func updateUIView(_ uiView: UIScrollView, context: Context) {
        context.coordinator.hostingController.rootView = self.content
        assert(context.coordinator.hostingController.view.superview == uiView)
    }
    
    // MARK: - Coordinator
    
    class Coordinator: NSObject, UIScrollViewDelegate {
        var hostingController: UIHostingController<Content>
        
        init(hostingController: UIHostingController<Content>) {
            self.hostingController = hostingController
        }
        
        func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return hostingController.view
        }
        
        func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
            if(scale < scrollView.minimumZoomScale){
                scrollView.minimumZoomScale = scale
            }
            
            if(scale > scrollView.maximumZoomScale){
                scrollView.maximumZoomScale = scale
            }
            
            
            
            
            
            
            
            
        }
    }
}

我可以成功地缩小到超出子视图边界的范围,但是当发生这种情况时,子视图从屏幕左上角缩小并开始出现奇怪的行为。有没有办法a)使子视图从屏幕中心缩小然后扩展到新边界或b)也许在开始时将子视图的比例设置为较小的值然后设置合理的最小缩放?

好的,我找到了答案。基本上,根据 Whosebug 的其他成员的说法,每次捏合时都必须动态更改 contentOffset 和 contentInset 变量,以便管理内容锚定缩放的位置。这是使这项工作生效的更改代码:

//
//  MapWrapperView.swift
//  Saturday
//
//  Created by Bruce Jagid on 6/22/21.
//

import SwiftUI
import UIKit



struct MapWrapperView<Content: View>: UIViewRepresentable {
    private var content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    func makeUIView(context: Context) -> UIScrollView {
        let scrollView = UIScrollView()
        scrollView.delegate = context.coordinator
        scrollView.minimumZoomScale = 0
        scrollView.maximumZoomScale = 100
        scrollView.bouncesZoom = false
        scrollView.bounces = false
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.clipsToBounds = false
        
        let hostedView = context.coordinator.hostingController.view!
        hostedView.translatesAutoresizingMaskIntoConstraints = true
        hostedView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        scrollView.bounds = hostedView.frame
        scrollView.addSubview(hostedView)
        
        let leftMargin: CGFloat = (scrollView.frame.size.width - hostedView.bounds.width)*0.5
        let topMargin: CGFloat = (scrollView.frame.size.height - hostedView.bounds.height)*0.5
        
        scrollView.contentOffset = CGPoint(x: max(0,-leftMargin), y: max(0,-topMargin));
        scrollView.contentSize = CGSize(width: max(hostedView.bounds.width, hostedView.bounds.width+1), height: max(hostedView.bounds.height, hostedView.bounds.height+1))
        
        return scrollView
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(hostingController: UIHostingController(rootView: self.content))
    }
    
    func updateUIView(_ uiView: UIScrollView, context: Context) {
        context.coordinator.hostingController.rootView = self.content
        assert(context.coordinator.hostingController.view.superview == uiView)
    }
    
    // MARK: - Coordinator
    
    class Coordinator: NSObject, UIScrollViewDelegate {
        var hostingController: UIHostingController<Content>
        
        init(hostingController: UIHostingController<Content>) {
            self.hostingController = hostingController
        }
        
        func viewForZooming(in scrollView: UIScrollView) -> UIView? {
            return hostingController.view
        }
        
        func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
            if(scale < scrollView.minimumZoomScale){
                scrollView.minimumZoomScale = scale
            }
            
            if(scale > scrollView.maximumZoomScale){
                scrollView.maximumZoomScale = scale
            }
            
        }
        
        func scrollViewDidZoom(_ scrollView: UIScrollView) {
            if(scrollView.zoomScale < 1){
                let leftMargin: CGFloat = (scrollView.frame.size.width - hostingController.view!.frame.width)*0.5
                let topMargin: CGFloat = (scrollView.frame.size.height - hostingController.view!.frame.height)*0.5
                scrollView.contentInset = UIEdgeInsets(top: max(0, topMargin), left: max(0,leftMargin), bottom: 0, right: 0)
            }
            
        }
    }
}

每当滚动视图缩小超过内容视图边界时,缩放将居中,按照 scrollViewDidZoom 委托函数中的逻辑。