有没有一种简单的方法可以在 SwiftUI 中捏合缩放和拖动任何视图?

Ant*_*lvo 1 uikit gesture drag pinch swiftui

我一直在寻找一段简短的、可重用的代码,它允许在 SwiftUI 中缩放和拖动任何视图,并且还可以独立更改比例。

Ant*_*lvo 6

这就是答案。

我添加的有趣的部分是,可以通过绑定属性从外部控制缩放视图的比例。因此,我们不需要仅仅依赖于捏合手势,而是可以添加双击来获得最大比例,返回到正常比例,或者使用滑块(例如)来根据需要更改比例。

我将这段代码的大部分归功于jtbandes对这个问题的回答。

这里,您在单个文件中包含可缩放和可滚动视图的代码以及一个测试视图以显示其工作原理:

`

import SwiftUI

let maxAllowedScale = 4.0

struct TestZoomableScrollView: View {

    @State private var scale: CGFloat = 1.0
    
    var doubleTapGesture: some Gesture {
        TapGesture(count: 2).onEnded {
            if scale < maxAllowedScale / 2 {
                scale = maxAllowedScale
            } else {
                scale = 1.0
            }
        }
    }
    
    var body: some View {
            VStack(alignment: .center) {
                Spacer()
                ZoomableScrollView(scale: $scale) {
                    Image("foto_producto")
                        .resizable()
                        .scaledToFit()
                        .frame(width: 200, height: 200)
                }
                .frame(width: 300, height: 300)
                .border(.black)
                .gesture(doubleTapGesture)
                Spacer()
                Text("Change the scale")
                Slider(value: $scale, in: 0.5...maxAllowedScale + 0.5)
                .padding(.horizontal)
                Spacer()
            }
    }
}

struct ZoomableScrollView<Content: View>: UIViewRepresentable {
    
    private var content: Content
    @Binding private var scale: CGFloat

    init(scale: Binding<CGFloat>, @ViewBuilder content: () -> Content) {
        self._scale = scale
        self.content = content()
    }

    func makeUIView(context: Context) -> UIScrollView {
        // set up the UIScrollView
        let scrollView = UIScrollView()
        scrollView.delegate = context.coordinator  // for viewForZooming(in:)
        scrollView.maximumZoomScale = maxAllowedScale
        scrollView.minimumZoomScale = 1
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.bouncesZoom = true

//      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)

        return scrollView
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(hostingController: UIHostingController(rootView: self.content), scale: $scale)
    }

    func updateUIView(_ uiView: UIScrollView, context: Context) {
        // update the hosting controller's SwiftUI content
        context.coordinator.hostingController.rootView = self.content
        uiView.zoomScale = scale
        assert(context.coordinator.hostingController.view.superview == uiView)
    }
    
    class Coordinator: NSObject, UIScrollViewDelegate {

        var hostingController: UIHostingController<Content>
        @Binding var scale: CGFloat

        init(hostingController: UIHostingController<Content>, scale: Binding<CGFloat>) {
            self.hostingController = hostingController
            self._scale = scale
        }

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

        func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
            self.scale = scale
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

`

我认为这是获得所需行为的最短、最简单的方法。此外,它工作得非常完美,这是我在此处提供的其他解决方案中找不到的。例如,缩小是平滑的,但如果不使用这种方法,通常会出现不稳定的情况。

滑块具有该范围以显示如何尊重最小值和最大值,在真实应用程序中,范围将为 1...maxAllowedScale。

至于双击,可以根据您的喜好轻松更改其行为。

我附上视频以一次性展示所有内容:

在此输入图像描述

我希望这对任何正在寻找此功能的人有所帮助。