Как уменьшить масштаб за пределами границ просмотра с помощью UIScrollView в swiftui

#ios #swift #swiftui #uikit

Вопрос:

Я пытаюсь создать 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
            }
            
            
            
            
            
            
            
            
        }
    }
}
 

Я могу успешно уменьшить масштаб дальше, чем границы подвид, но когда это происходит, подвид уменьшается с верхнего левого края экрана и начинает вести себя странно. Есть ли какой-либо способ: а) уменьшить масштаб подвида от центра экрана, а затем расширить его до новых границ, или б) возможно, вначале установить масштаб подвида на меньшее значение, а затем установить разумный минимальный масштаб?

Ответ №1:

Хорошо, я нашел ответ. В принципе, согласно другим членам stackoverflow, переменная 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.

Комментарии:

1. Я предполагаю, что мой следующий вопрос заключается в том, как сделать так, чтобы этот вид все еще позволял мне увеличивать определенные области за пределами подвида?

2. Неважно, я тоже это понял, просто сделал начальный масштаб подвида небольшим, поэтому он начинает уменьшаться. Вид прокрутки автоматически отображает это в масштабе 1, поэтому увеличение и уменьшение масштаба ничего не меняет