如何在 SwiftUI 中捏合和滚动图像?

How to pinch and scroll an image in SwiftUI?

我想在我的应用程序中添加一个图像查看器,但我无法实现它。我想显示一个图像并允许用户在图像中捏合和滚动以详细检查它。根据我从多个 Internet 帖子中收集到的内容,我有一些工作,但不完全是。这是我的代码:

import SwiftUI

struct ContentView: View {

    @State var currentScale: CGFloat = 1.0
    @State var previousScale: CGFloat = 1.0

    @State var currentOffset = CGSize.zero
    @State var previousOffset = CGSize.zero

    var body: some View {

        ZStack {

            Image("bulldog2")
                .resizable()
                .edgesIgnoringSafeArea(.all)
                .aspectRatio(contentMode: .fit)
                .offset(x: self.currentOffset.width, y: self.currentOffset.height)
                .scaleEffect(self.currentScale)
                .gesture(DragGesture()
                    .onChanged { value in
                        let deltaX = value.translation.width - self.previousOffset.width
                        let deltaY = value.translation.height - self.previousOffset.height
                        self.previousOffset.width = value.translation.width
                        self.previousOffset.height = value.translation.height
                        self.currentOffset.width = self.currentOffset.width + deltaX / self.currentScale
                        self.currentOffset.height = self.currentOffset.height + deltaY / self.currentScale }
                    .onEnded { value in self.previousOffset = CGSize.zero })
                .gesture(MagnificationGesture()
                    .onChanged { value in
                        let delta = value / self.previousScale
                        self.previousScale = value
                        self.currentScale = self.currentScale * delta
                }
                .onEnded { value in self.previousScale = 1.0 })

            VStack {

                Spacer()

                HStack {
                    Text("Menu 1").padding().background(Color.white).cornerRadius(30).padding()
                    Spacer()
                    Text("Menu 2").padding().background(Color.white).cornerRadius(30).padding()
                }
            }
        }
    }
}

初始视图如下所示:

我遇到的第一个问题是我可以将图像移动得太远,以至于我可以看到图像外部。如果图像移动得太远,这可能会导致图像在应用程序中不再可见。

第二个问题不是很大,我可以缩小图像但与视图相比它变得太小了。我想限制它,使 "fit" 成为它的最小尺寸。有没有比约束 self.currentScaleself.previousScale 更好的方法?

第三个问题是,如果我更改图像以填充 space,底部菜单会变得比 phone 的屏幕大。

我不是 iOS 开发人员,可能有更好的方法来实现此功能。谢谢你的帮助。

我可以回答 3 个问题中的 2 个。第三个不能重复了

  1. 您可以使用 GeometryReader 并使用它的 framesize 进行一些限制(我将在下面的示例中展示);
  2. 也许最简单和更好的方法就是使用 max 函数,例如 .scaleEffect(max(self.currentScale, 1.0)).

这里是改变的例子:

struct BulldogImageViewerView: View {
    @State var currentScale: CGFloat = 1.0
    @State var previousScale: CGFloat = 1.0

    @State var currentOffset = CGSize.zero
    @State var previousOffset = CGSize.zero

    var body: some View {

        ZStack {

            GeometryReader { geometry in // here you'll have size and frame

                Image("bulldog")
                    .resizable()
                    .edgesIgnoringSafeArea(.all)
                    .aspectRatio(contentMode: .fit)
                    .offset(x: self.currentOffset.width, y: self.currentOffset.height)
                    .scaleEffect(max(self.currentScale, 1.0)) // the second question
                    .gesture(DragGesture()
                        .onChanged { value in

                            let deltaX = value.translation.width - self.previousOffset.width
                            let deltaY = value.translation.height - self.previousOffset.height
                            self.previousOffset.width = value.translation.width
                            self.previousOffset.height = value.translation.height

                            let newOffsetWidth = self.currentOffset.width + deltaX / self.currentScale
                            // question 1: how to add horizontal constraint (but you need to think about scale)
                            if newOffsetWidth <= geometry.size.width - 150.0 && newOffsetWidth > -150.0 {
                                self.currentOffset.width = self.currentOffset.width + deltaX / self.currentScale
                            }

                            self.currentOffset.height = self.currentOffset.height + deltaY / self.currentScale }

                        .onEnded { value in self.previousOffset = CGSize.zero })

                    .gesture(MagnificationGesture()
                        .onChanged { value in
                            let delta = value / self.previousScale
                            self.previousScale = value
                            self.currentScale = self.currentScale * delta
                    }
                    .onEnded { value in self.previousScale = 1.0 })

            }

            VStack {

                Spacer()

                HStack {
                    Text("Menu 1").padding().cornerRadius(30).background(Color.blue).padding()
                    Spacer()
                    Text("Menu 2").padding().cornerRadius(30).background(Color.blue).padding()
                }
            }
        }
    }
}

用这段代码我实现了:

  • 用户不能水平方向将图片移离屏幕;

  • 用户不能缩小图片;