你好,在我的应用程序中,我为用户提供了按下图像以全屏显示的选项.当图像以全屏显示时,用户可以放大它,并应该能够拖动它来查看图像的其他部分.目前,当图像被放大时,我试图将其拖动到任何一侧,它会重新居中,拖拽效果不佳.另外,我只能放大到中心.我如何修改这段代码,以便在放大时向任何一侧的拖动手势都是平滑的,并且可以在图像的两侧而不仅仅是中心进行zoom 手势.我很感谢你的帮助.KFImage是一个简单的包,可以从url渲染图像,您可以使用以下url:https://github.com/onevcat/Kingfisher.git将其快速添加到Xcode

import SwiftUI
import Kingfisher
import UIKit

struct ContentView: View {
    @State private var myPhoto = "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F7AC5914A-6239-41CF-85EF-E1C0F25C0A84?alt=media&token=8720789b-7cdd-410c-9532-143a2bcf3f3b"
    @State private var currentScale: CGFloat = 1.0
    @State private var previousScale: CGFloat = 1.0
    @State private var currentOffset = CGSize.zero
    @State private var previousOffset = CGSize.zero
    @State var imageHeight: Double = 0.0
    @State var imageWidth: Double = 0.0
    
    var body: some View {
        ZStack {
            Rectangle().foregroundColor(.black)
            GeometryReader { geometry in
                KFImage(URL(string: myPhoto))
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(max(self.currentScale, 1.0))
                    .offset(x: self.currentOffset.width + (widthOrHeight(width: true) - imageWidth) / 2.0, y: self.currentOffset.height + (widthOrHeight(width: false) - imageHeight) / 2.0)
                    .background ( /// this background is to center the image (image loaded async so have to wait for it to load and then get height)
                        GeometryReader { proxy in
                            Color.clear
                                .onChange(of: proxy.size.height) { _ in
                                    imageHeight = proxy.size.height
                                    imageWidth = proxy.size.width
                                }
                            
                        }
                    )
                    .gesture(
                        SimultaneousGesture(
                            DragGesture()
                                .onChanged { value in
                                    let deltaX = value.translation.width - self.previousOffset.width
                                    let deltaY = value.translation.height - self.previousOffset.height
                                    previousOffset.width = value.translation.width
                                    previousOffset.height = value.translation.height
                                    let newOffsetWidth = self.currentOffset.width + deltaX / self.currentScale
                                    let newOffsetHeight = self.currentOffset.height + deltaY / self.currentScale
                                    withAnimation(.linear(duration: 0.25)){
                                        if abs(newOffsetWidth) <= geometry.size.width - 200.0 && abs(newOffsetWidth) >= -200.0 {
                                            self.currentOffset.width = newOffsetWidth
                                        }
                                        if abs(newOffsetHeight) < 450 {
                                            self.currentOffset.height = newOffsetHeight
                                        }
                                    }
                                }
                                .onEnded { _ in
                                    withAnimation(.easeInOut){
                                        if currentScale < 1.2 {
                                            self.previousOffset = CGSize.zero
                                            self.currentOffset = CGSize.zero
                                        }
                                    }
                                },
                            MagnificationGesture().onChanged { value in
                                let delta = value / self.previousScale
                                let newScale = self.currentScale * delta
                                withAnimation {
                                    if newScale <= 3.5 {
                                        self.previousScale = value
                                        self.currentScale = newScale
                                    }
                                    if newScale < 1.3 {
                                        self.previousScale = 1.0
                                        self.currentScale = 1.0
                                        self.previousOffset = CGSize.zero
                                        self.currentOffset = CGSize.zero
                                    }
                                }
                            }
                        )
                    )
            }
        }.ignoresSafeArea()
    }
}

推荐答案

看一下a similar implementation,您可以try 使用条件判断来将视图保持在边界内.

类似于:

import SwiftUI
import Kingfisher

struct ContentView: View {
    @State private var myPhoto = "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F7AC5914A-6239-41CF-85EF-E1C0F25C0A84?alt=media&token=8720789b-7cdd-410c-9532-143a2bcf3f3b"
    @State private var currentScale: CGFloat = 1.0 // Removed previousScale
    @State private var currentOffset = CGSize.zero  // Removed previousOffset
    
    var body: some View {
        ZStack {
            Rectangle().foregroundColor(.black)
            GeometryReader { geometry in // Use dynamic offsets and scaling factors based on GeometryReader
                KFImage(URL(string: myPhoto))
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .scaleEffect(currentScale) // Removed max scaling, use currentScale directly for smoother scaling
                    .offset(x: currentOffset.width, y: currentOffset.height) // Use currentOffset directly for smoother dragging
                    .gesture(
                        SimultaneousGesture(
                            DragGesture()
                                .onChanged { value in
                                    // Calculate dynamic offset while dragging
                                    self.currentOffset.width = value.translation.width / self.currentScale
                                    self.currentOffset.height = value.translation.height / self.currentScale
                                }
                                .onEnded { value in
                                    // Set previousOffset to currentOffset to maintain position
                                },
                            MagnificationGesture()
                                .onChanged { value in
                                    // Calculate dynamic scaling
                                    let newScale = self.currentScale * value
                                    self.currentScale = min(max(newScale, 1.0), 3.0) // Use min-max to restrict scaling
                                }
                                .onEnded { value in
                                    // Reset to default scaling for next gesture
                                    self.currentScale = 1.0
                                }
                        )
                    )
                    .clipped() // Clip the view to its bounding frame
                    .onAppear {
                        // Reset on appear
                        self.currentScale = 1.0
                        self.currentOffset = CGSize.zero
                    }
            }
        }.ignoresSafeArea()
    }
}

但也可以考虑另一种方法,使用ZoomableContainer presented here.

Ios相关问答推荐

不使用iOS 17修改器修改SWIFT用户界面视图

AVFoundation Camera推出变焦SWIFT

无法添加以供审阅-Xcode 15.0.1

WatchConnectivity - 从手表配套应用程序在 iOS 设备上启动 AVPlayer

Swift Vision库识别文本后如何将其赋值给 struct 体实例属性以供显示?

为什么 Firebase Messaging 配置对于 IOS native 和 Flutter iOS 不同?

由于缺少 libarclite_iphonesimulator.a 文件,将 XCode 升级到 14.3 后无法在模拟器上运行 Flutter 应用程序

SwiftUI 解除 .alert 弹出 NavigationView

.onAppear() 函数不适用于 NavigationLink

如何在不支持并发的自动关闭中修复'async'调用?

通过隐藏和显示视图在 iMessage 应用程序中使用不同的视图

UICollectionView - 水平滚动,水平布局?

界面生成器 - 无法从路径加载设计对象(空)

Xcode 缺少支持文件 iOS 12.2 (16E227)

在 iPhone 中将 UIViewController 显示为弹出窗口

iOS 7 圆形框架按钮

为所有 UIImageViews 添加圆角

以编程方式确定 iPhone 是否越狱

你如何以编程方式从视图控制器中画一条线?

dismissModalViewController 并传回数据