在 Swiftui 中是不是有一种简单的方法可以通过捏合来放大图像?
Posted
技术标签:
【中文标题】在 Swiftui 中是不是有一种简单的方法可以通过捏合来放大图像?【英文标题】:Isn't there an easy way to pinch to zoom in an image in Swiftui?在 Swiftui 中是否有一种简单的方法可以通过捏合来放大图像? 【发布时间】:2019-10-11 13:00:32 【问题描述】:我希望能够在 SwiftUI 中调整和移动图像(就像它是地图一样),通过捏合来缩放和拖动它。
使用 UIKit,我将图像嵌入到 UIScrollView
中,它会处理它,但我不知道如何在 SwiftUI 中执行此操作。
我尝试使用MagnificationGesture
,但无法使其顺利运行。
我已经搜索了一段时间了,有人知道是否有更简单的方法吗?
【问题讨论】:
做同样的事情,将图像添加到滚动视图中。 SwiftUI 仍然可以使用滚动视图 不知道 SwiftUI 中的 ScrollView 是否原生支持缩放。或者我只是找不到它。我得到了这个,但它的行为很奇怪。 ScrollView(Axis.Set(arrayLiteral: [.horizontal, .vertical]), showsIndicators: false) Image("building").resizable().scaledToFit().scaleEffect(self.scale) .gesture(MagnificationGesture() .onChanged scale in self.scale = scale ) 对于仍在寻找的任何人,唯一有效的答案来自 jtbandes - 尽管我建议您查看他在 GitHub 中的工作示例。我已经查看了网络上的每篇文章,了解如何在本地执行此操作,但这根本不可能。正确计算各种变量(中心、锚点、边界等)所需的状态以及将拖动和缩放合并在一起的能力,在 SwiftUI 2.0 版本中根本不存在。我已经为图像实现了 jtbandes 解决方案,它工作得非常好,就像 Apple Photos 一样。 Pastebin 链接:pastebin.com/embed_js/rpSRTddm 【参考方案1】:这里的其他答案对于自定义缩放逻辑过于复杂。如果您想要经过实战考验的标准 UIScrollView 缩放行为,您可以使用 UIScrollView!
SwiftUI 允许您使用 UIViewRepresentable
或 UIViewControllerRepresentable
将任何 UIView 放入其他 SwiftUI 视图层次结构中。然后要将更多 SwiftUI 内容放入该视图中,您可以使用 UIHostingController
。在Interfacing with UIKit 和API docs 中阅读有关 SwiftUI–UIKit 互操作的更多信息。
您可以在https://github.com/jtbandes/SpacePOD/blob/main/SpacePOD/ZoomableScrollView.swift 找到我在实际应用中使用此功能的更完整示例(该示例还包括更多使图像居中的技巧。)
var body: some View
ZoomableScrollView
Image("Your image here")
struct ZoomableScrollView<Content: View>: UIViewRepresentable
private var content: Content
init(@ViewBuilder content: () -> Content)
self.content = content()
func makeUIView(context: Context) -> UIScrollView
// set up the UIScrollView
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator // for viewForZooming(in:)
scrollView.maximumZoomScale = 20
scrollView.minimumZoomScale = 1
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))
func updateUIView(_ uiView: UIScrollView, context: Context)
// update the hosting controller's SwiftUI content
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
【讨论】:
这行得通,我知道,但我认为你误解了这个问题。我在寻求一种简单的“原生”方式来使用 SwiftUI,而不使用 UIKit。无论如何感谢您的回复!可能对许多人有用。 我确实希望 SwiftUI 的 ScrollView 尽快获得此功能,但与此同时,我认为没有太多理由更喜欢“本机”(纯 SwiftUI)解决方案而不是混合一些 UIKit 的解决方案。逃生舱的存在是有原因的!我将在我的项目中使用它:) 很棒的样品!我一直在尝试使用原生 SwiftUI 甚至是带有手势的 UIKit 视图,而 scaleEffect 刚刚损坏。但是,尝试使用您的代码时,我会在放置图像时得到一个非常奇怪的“跳跃”或“捕捉”。我认为图像与它没有任何关系,但是您的代码非常简单。你有过这样的经历吗? 感激不尽,这完全救了我!我采用了您在上面发布的代码并进行了一些修改以供我使用,即我添加了一个 UIImageView 而不是嵌入式 SwiftUI 视图,这就是我所需要的。我不得不迎合安全区域等,但结果非常棒。我看了你的美国宇航局照片项目,当我有更多时间时,我会考虑将它整合到我的其他非图像项目中。再次,谢谢。 完美!谢谢【参考方案2】:SwiftUI API 在这里非常无用:onChanged 给出了相对于当前缩放手势开始的数字,并且在回调中没有明显的方法来获取初始值。还有一个 onEnded 回调,但很容易错过/忘记。
解决方法,添加:
@State var lastScaleValue: CGFloat = 1.0
然后在回调中:
.gesture(MagnificationGesture().onChanged val in
let delta = val / self.lastScaleValue
self.lastScaleValue = val
let newScale = self.scale * delta
//... anything else e.g. clamping the newScale
.onEnded val in
// without this the next gesture will be broken
self.lastScaleValue = 1.0
其中 newScale 是您自己对比例的跟踪(可能是状态或绑定)。如果你直接设置你的比例,它会变得一团糟,因为在每个刻度上,金额都将与之前的金额相关。
【讨论】:
它实际上更...自然。现在我很好地理解了 onChange 值和 onEnded 值。谢谢! :D【参考方案3】:这是向 SwiftUI 视图添加捏合缩放的一种方法。它将UIView
与UIPinchGestureRecognizer
覆盖在UIViewRepresentable
中,并通过绑定将相关值转发回SwiftUI。
您可以像这样添加行为:
Image("Zoom")
.pinchToZoom()
这增加了类似于在 Instagram 供稿中缩放照片的行为。完整代码如下:
import UIKit
import SwiftUI
class PinchZoomView: UIView
weak var delegate: PinchZoomViewDelgate?
private(set) var scale: CGFloat = 0
didSet
delegate?.pinchZoomView(self, didChangeScale: scale)
private(set) var anchor: UnitPoint = .center
didSet
delegate?.pinchZoomView(self, didChangeAnchor: anchor)
private(set) var offset: CGSize = .zero
didSet
delegate?.pinchZoomView(self, didChangeOffset: offset)
private(set) var isPinching: Bool = false
didSet
delegate?.pinchZoomView(self, didChangePinching: isPinching)
private var startLocation: CGPoint = .zero
private var location: CGPoint = .zero
private var numberOfTouches: Int = 0
init()
super.init(frame: .zero)
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(pinch(gesture:)))
pinchGesture.cancelsTouchesInView = false
addGestureRecognizer(pinchGesture)
required init?(coder: NSCoder)
fatalError()
@objc private func pinch(gesture: UIPinchGestureRecognizer)
switch gesture.state
case .began:
isPinching = true
startLocation = gesture.location(in: self)
anchor = UnitPoint(x: startLocation.x / bounds.width, y: startLocation.y / bounds.height)
numberOfTouches = gesture.numberOfTouches
case .changed:
if gesture.numberOfTouches != numberOfTouches
// If the number of fingers being used changes, the start location needs to be adjusted to avoid jumping.
let newLocation = gesture.location(in: self)
let jumpDifference = CGSize(width: newLocation.x - location.x, height: newLocation.y - location.y)
startLocation = CGPoint(x: startLocation.x + jumpDifference.width, y: startLocation.y + jumpDifference.height)
numberOfTouches = gesture.numberOfTouches
scale = gesture.scale
location = gesture.location(in: self)
offset = CGSize(width: location.x - startLocation.x, height: location.y - startLocation.y)
case .ended, .cancelled, .failed:
isPinching = false
scale = 1.0
anchor = .center
offset = .zero
default:
break
protocol PinchZoomViewDelgate: AnyObject
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangePinching isPinching: Bool)
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeScale scale: CGFloat)
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeAnchor anchor: UnitPoint)
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeOffset offset: CGSize)
struct PinchZoom: UIViewRepresentable
@Binding var scale: CGFloat
@Binding var anchor: UnitPoint
@Binding var offset: CGSize
@Binding var isPinching: Bool
func makeCoordinator() -> Coordinator
Coordinator(self)
func makeUIView(context: Context) -> PinchZoomView
let pinchZoomView = PinchZoomView()
pinchZoomView.delegate = context.coordinator
return pinchZoomView
func updateUIView(_ pageControl: PinchZoomView, context: Context)
class Coordinator: NSObject, PinchZoomViewDelgate
var pinchZoom: PinchZoom
init(_ pinchZoom: PinchZoom)
self.pinchZoom = pinchZoom
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangePinching isPinching: Bool)
pinchZoom.isPinching = isPinching
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeScale scale: CGFloat)
pinchZoom.scale = scale
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeAnchor anchor: UnitPoint)
pinchZoom.anchor = anchor
func pinchZoomView(_ pinchZoomView: PinchZoomView, didChangeOffset offset: CGSize)
pinchZoom.offset = offset
struct PinchToZoom: ViewModifier
@State var scale: CGFloat = 1.0
@State var anchor: UnitPoint = .center
@State var offset: CGSize = .zero
@State var isPinching: Bool = false
func body(content: Content) -> some View
content
.scaleEffect(scale, anchor: anchor)
.offset(offset)
.animation(isPinching ? .none : .spring())
.overlay(PinchZoom(scale: $scale, anchor: $anchor, offset: $offset, isPinching: $isPinching))
extension View
func pinchToZoom() -> some View
self.modifier(PinchToZoom())
【讨论】:
嗨,我打算做一些更像 Facebook 捏的东西,女巫保持放大,你可以滚动。我首先尝试在pinch
函数中调整您的代码,将.ended
与.canceled
和.failed
分开,并从.ended
案例中删除scale
。有了这个改变,我可以保持比例,但我不能缩小(使用捏)也不能环顾四周,你能指出我错过了什么吗?我扫描了您的代码几次,无法弄清楚还需要什么。在我看来,至少捏出应该可以工作,然后只需添加拖动手势。
嗨@PedroCavaleiro。我认为这段代码不适用于这个用例。我建议您改用 UIScrollView
及其缩放功能。
我真的很喜欢这个解决方案,但它会阻止内容视图中的所有 SwiftUI 手势识别器。如果内容视图是可表示的,它似乎也阻止了它的所有 UIGestureRecognizers。
你刚刚救了我的命!!谢谢你:)
您认为您可以在您的班级中添加 2 个手指翻译吗?这将允许在用户定义的中心进行缩放。例如,对于绘图应用程序非常有用!【参考方案4】:
在 SwiftUI 的 ScrollView 中似乎没有原生支持,但是,仍然有一个非常简单的方法可以做到这一点。
像你想要的那样创建一个MagnificationGesture
,但一定要把你当前的比例乘以你在手势的.onChanged
闭包中得到的值。此闭包为您提供缩放变化,而不是当前的缩放值。
当您缩小并开始放大时,它不会从当前比例增加(0.5 到 0.6 作为任意示例),它会从 1 增加到 1.1。这就是你看到奇怪行为的原因。
如果MagnificationGesture
位于与.scaleEffect
相同的视图上,此答案将有效。否则,詹姆斯的答案会更好。
struct ContentView: View
@State var scale: CGFloat
var body: some View
let gesture = MagnificationGesture(minimumScaleDelta: 0.1)
.onChanged scaleDelta in
self.scale *= scaleDelta
return ScrollView
// Your ScrollView content here :)
.gesture(gesture)
.scaleEffect(scale)
附:您可能会发现为此目的使用ScrollView
很笨拙,并且您无法同时拖动和缩放。如果是这种情况并且您对此不满意,我会考虑添加多个手势并手动调整内容的偏移量,而不是使用 ScrollView
。
【讨论】:
我认为这行不通。回调的比例是相对的,所以手势的开始。因此,将每个回调乘以 delta 会使事情变得混乱,例如如果你扩大到两倍大小,那么在每个刻度上它都会让你的比例加倍。可能不是你想要的。 在某些情况下确实如此。这取决于您如何设置层次结构。如果手势不在缩放的视图上,您将需要使用您的答案,如果手势在缩放的同一视图上,我的回答可以解决问题:)【参考方案5】:我也在为这个问题而苦恼。但是这个视频制作了一些工作样本-(https://www.youtube.com/watch?v=p0SwXJYJp2U)
这还没有完成。很难用锚点进行缩放。希望这是对其他人的暗示。
struct ContentView: View
let maxScale: CGFloat = 3.0
let minScale: CGFloat = 1.0
@State var lastValue: CGFloat = 1.0
@State var scale: CGFloat = 1.0
@State var draged: CGSize = .zero
@State var prevDraged: CGSize = .zero
@State var tapPoint: CGPoint = .zero
@State var isTapped: Bool = false
var body: some View
let magnify = MagnificationGesture(minimumScaleDelta: 0.2)
.onChanged value in
let resolvedDelta = value / self.lastValue
self.lastValue = value
let newScale = self.scale * resolvedDelta
self.scale = min(self.maxScale, max(self.minScale, newScale))
print("delta=\(value) resolvedDelta=\(resolvedDelta) newScale=\(newScale)")
let gestureDrag = DragGesture(minimumDistance: 0, coordinateSpace: .local)
.onChanged (value) in
self.tapPoint = value.startLocation
self.draged = CGSize(width: value.translation.width + self.prevDraged.width,
height: value.translation.height + self.prevDraged.height)
return GeometryReader geo in
Image("dooli")
.resizable().scaledToFit().animation(.default)
.offset(self.draged)
.scaleEffect(self.scale)
// .scaleEffect(self.isTapped ? 2 : 1,
// anchor: UnitPoint(x: self.tapPoint.x / geo.frame(in: .local).maxX,
// y: self.tapPoint.y / geo.frame(in: .local).maxY))
.gesture(
TapGesture(count: 2).onEnded(
self.isTapped.toggle()
if self.scale > 1
self.scale = 1
else
self.scale = 2
let parent = geo.frame(in: .local)
self.postArranging(translation: CGSize.zero, in: parent)
)
.simultaneously(with: gestureDrag.onEnded( (value) in
let parent = geo.frame(in: .local)
self.postArranging(translation: value.translation, in: parent)
)
))
.gesture(magnify.onEnded value in
// without this the next gesture will be broken
self.lastValue = 1.0
let parent = geo.frame(in: .local)
self.postArranging(translation: CGSize.zero, in: parent)
)
.frame(height: 300)
.clipped()
.background(Color.gray)
private func postArranging(translation: CGSize, in parent: CGRect)
let scaled = self.scale
let parentWidth = parent.maxX
let parentHeight = parent.maxY
let offset = CGSize(width: (parentWidth * scaled - parentWidth) / 2,
height: (parentHeight * scaled - parentHeight) / 2)
print(offset)
var resolved = CGSize()
let newDraged = CGSize(width: self.draged.width * scaled,
height: self.draged.height * scaled)
if newDraged.width > offset.width
resolved.width = offset.width / scaled
else if newDraged.width < -offset.width
resolved.width = -offset.width / scaled
else
resolved.width = translation.width + self.prevDraged.width
if newDraged.height > offset.height
resolved.height = offset.height / scaled
else if newDraged.height < -offset.height
resolved.height = -offset.height / scaled
else
resolved.height = translation.height + self.prevDraged.height
self.draged = resolved
self.prevDraged = resolved
【讨论】:
希望苹果以后能提供一种标准简单的方式来做这些拖拽操作。注意,在 SwiftUI 最新版本中,simultaneously
被重命名为 simultaneousGesture
。【参考方案6】:
其他答案很好,这里有一个额外的提示:如果您使用的是 SwiftUI 手势,您可以使用 @GestureState
而不是 @State
来存储手势状态。它会在手势结束后自动将状态重置为初始值,因此您可以简化这种代码:
@State private var scale: CGFloat = 1.0
.gesture(MagnificationGesture().onChanged value in
// Anything with value
scale = value
.onEnded value in
scale = 1.0
)
与:
@GestureState private var scale: CGFloat = 1.0
.gesture(MagnificationGesture().updating($scale) (newValue, scale, _) in
// Anything with value
scale = newValue
)
【讨论】:
【参考方案7】:我认为值得一提的极其简单的方法 - 使用 Apple 的 PDFKit
。
import SwiftUI
import PDFKit
struct PhotoDetailView: UIViewRepresentable
let image: UIImage
func makeUIView(context: Context) -> PDFView
let view = PDFView()
view.document = PDFDocument()
guard let page = PDFPage(image: image) else return view
view.document?.insert(page, at: 0)
view.autoScales = true
return view
func updateUIView(_ uiView: PDFView, context: Context)
// empty
优点:
需要 0 个逻辑 感觉很专业 由 Apple 编写(未来不太可能中断)如果您只是展示图像以供查看,则此方法可能非常适合您。但是,如果您想添加图像注释等,我会遵循其他答案之一。
根据 maka 的建议编辑添加 view.autoScales = true
。
【讨论】:
你说得对,这对我来说是完美的。谢谢! 想补充一点,它为我解决了这个问题,但这条线有帮助,因为我的图像以一个巨大的缩放开始(这是一个高分辨率图像):view.autoScales = true
缩放看起来不错,但不能拖动,它总是会回到中心。
感谢您的提及。它解决了我的问题。干杯!
这确实是最好的方法。其他一切都感觉很笨拙,代码太多......谢谢【参考方案8】:
这是@James 接受响应的完整示例,它还具有基本支持,通过调整隐藏矩形来滚动新缩放的图像,该矩形会根据图像比例调整滚动视图的内容大小:
import SwiftUI
struct EnlargedImage: View
var image = UIImage(named: "YourImageName")
@State var scale: CGFloat = 1.0
@State var lastScaleValue: CGFloat = 1.0
var body: some View
ScrollView([.vertical, .horizontal], showsIndicators: false)
ZStack
Rectangle().foregroundColor(.clear).frame(width: image!.size.width * scale, height: image!.size.height * scale, alignment: .center)
Image(uiImage: image!).scaleEffect(scale)
.gesture(MagnificationGesture().onChanged val in
let delta = val / self.lastScaleValue
self.lastScaleValue = val
var newScale = self.scale * delta
if newScale < 1.0
newScale = 1.0
scale = newScale
.onEndedval in
lastScaleValue = 1
)
.background(Color(.systemBackground).edgesIgnoringSafeArea(.all))
我在my GitHub 有一个更好的版本。
【讨论】:
【参考方案9】:这是另一种解决方案,基于 jtbandes 的回答。它仍然将 UIScrollView
包装在 UIViewRepresentable
中,但有一些变化:
UIImage
,而不是通用的 SwiftUI 内容:它适用于这种情况,并且不需要将底层 UIImage
包装到 SwiftUI Image
它基于自动布局约束来布局图像视图,而不是自动调整掩码大小
它通过根据当前缩放级别计算顶部和前导约束的适当值,使图像居中于视图的中间
用途:
struct EncompassingView: View
let uiImage: UIImage
var body: some View
GeometryReader geometry in
ZoomableView(uiImage: uiImage, viewSize: geometry.size)
定义:
struct ZoomableView: UIViewRepresentable
let uiImage: UIImage
let viewSize: CGSize
private enum Constraint: String
case top
case leading
private var minimumZoomScale: CGFloat
let widthScale = viewSize.width / uiImage.size.width
let heightScale = viewSize.height / uiImage.size.height
return min(widthScale, heightScale)
func makeUIView(context: Context) -> UIScrollView
let scrollView = UIScrollView()
scrollView.delegate = context.coordinator
scrollView.maximumZoomScale = minimumZoomScale * 50
scrollView.minimumZoomScale = minimumZoomScale
scrollView.bouncesZoom = true
let imageView = UIImageView(image: uiImage)
scrollView.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
let topConstraint = imageView.topAnchor.constraint(equalTo: scrollView.topAnchor)
topConstraint.identifier = Constraint.top.rawValue
topConstraint.isActive = true
let leadingConstraint = imageView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor)
leadingConstraint.identifier = Constraint.leading.rawValue
leadingConstraint.isActive = true
imageView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
imageView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
return scrollView
func makeCoordinator() -> Coordinator
return Coordinator()
func updateUIView(_ scrollView: UIScrollView, context: Context)
guard let imageView = scrollView.subviews.first as? UIImageView else
return
// Inject dependencies into coordinator
context.coordinator.zoomableView = imageView
context.coordinator.imageSize = uiImage.size
context.coordinator.viewSize = viewSize
let topConstraint = scrollView.constraints.first $0.identifier == Constraint.top.rawValue
let leadingConstraint = scrollView.constraints.first $0.identifier == Constraint.leading.rawValue
context.coordinator.topConstraint = topConstraint
context.coordinator.leadingConstraint = leadingConstraint
// Set initial zoom scale
scrollView.zoomScale = minimumZoomScale
// MARK: - Coordinator
extension ZoomableView
class Coordinator: NSObject, UIScrollViewDelegate
var zoomableView: UIView?
var imageSize: CGSize?
var viewSize: CGSize?
var topConstraint: NSLayoutConstraint?
var leadingConstraint: NSLayoutConstraint?
func viewForZooming(in scrollView: UIScrollView) -> UIView?
zoomableView
func scrollViewDidZoom(_ scrollView: UIScrollView)
let zoomScale = scrollView.zoomScale
print("zoomScale = \(zoomScale)")
guard
let topConstraint = topConstraint,
let leadingConstraint = leadingConstraint,
let imageSize = imageSize,
let viewSize = viewSize
else
return
topConstraint.constant = max((viewSize.height - (imageSize.height * zoomScale)) / 2.0, 0.0)
leadingConstraint.constant = max((viewSize.width - (imageSize.width * zoomScale)) / 2.0, 0.0)
【讨论】:
【参考方案10】:这是@James 和@ethoooo 的另一种方法。最终缩放状态和瞬态手势状态是分开的(瞬态将始终返回 1),因此除了手势本身之外,您还可以通过按钮或步进器设置该状态。
@State var scrollContentZoom: CGFloat = 1
@GestureState var scrollContentGestureZoom: CGFloat = 1
var contentZoom: CGFloat scrollContentZoom*scrollContentGestureZoom
var magnification: some Gesture
MagnificationGesture()
.updating($scrollContentGestureZoom) state, gestureState, transaction in
print("Magnifed: \(state)")
gestureState = state
.onEnded (state) in
scrollContentZoom = contentZoom*state
【讨论】:
【参考方案11】:struct DetailView: View
var item: MenuItem
@State private var zoomed:Bool = false
@State var scale: CGFloat = 1.0
@State var isTapped: Bool = false
@State var pointTaped: CGPoint = CGPoint.zero
@State var draggedSize: CGSize = CGSize.zero
@State var previousDraged: CGSize = CGSize.zero
var width = UIScreen.main.bounds.size.width
var height = UIScreen.main.bounds.size.height
var body: some View
GeometryReader reader in
VStack(alignment: .center)
ScrollView()
HStack
ScrollView(.vertical)
Image(self.item.mainImage)
.resizable()
.scaledToFill()
.animation(.default).offset(x: self.draggedSize.width, y: 0)
.scaleEffect(self.scale).scaleEffect(self.isTapped ? 2 : 1, anchor: UnitPoint(x : (self.pointTaped.x) / (reader.frame(in : .global).maxX),y: (self.pointTaped.y) / (reader.frame(in : .global).maxY )))
.gesture(TapGesture(count: 2)
.onEnded( value in
self.isTapped = !self.isTapped
)
.simultaneously(with: DragGesture(minimumDistance: 0, coordinateSpace: .global) .onChanged (value) in
self.pointTaped = value.startLocation
self.draggedSize = CGSize(width: value.translation.width + self.previousDraged.width, height: value.translation.height + self.previousDraged.height)
.onEnded( (value) in
let offSetWidth = (reader.frame(in :.global).maxX * self.scale) - (reader.frame(in :.global).maxX) / 2
let newDraggedWidth = self.previousDraged.width * self.scale
if (newDraggedWidth > offSetWidth)
self.draggedSize = CGSize(width: offSetWidth / self.scale, height: value.translation.height + self.previousDraged.height)
else if (newDraggedWidth < -offSetWidth)
self.draggedSize = CGSize(width: -offSetWidth / self.scale, height: value.translation.height + self.previousDraged.height)
else
self.draggedSize = CGSize(width: value.translation.width + self.previousDraged.width, height: value.translation.height + self.previousDraged.height)
self.previousDraged = self.draggedSize
)))
.gesture(MagnificationGesture()
.onChanged (value) in
self.scale = value.magnitude
.onEnded (val) in
//self.scale = 1.0
self.scale = val.magnitude
)
HStack
Text(self.item.description)
.foregroundColor(Color.black)
.multilineTextAlignment(.leading)
.padding(4)
.navigationBarTitle("Menu Detail")
【讨论】:
虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性! 您的示例代码包含很多与问题无关的代码。这只会让我们更难提取重要部分。以上是关于在 Swiftui 中是不是有一种简单的方法可以通过捏合来放大图像?的主要内容,如果未能解决你的问题,请参考以下文章
ListView,是不是有一种简单的方法可以允许在内部拖动项目(内置)?
如果从某个 html 文件接近,是不是有一种简单的方法可以只执行 if 语句?
WPF Grid:如果我需要插入新行,是不是有一种简单的方法可以重新调整行项目?
当您使用 Python 的 Paramiko 库进行 SSH 并从远程机器的 CLI 获取输出时,是不是有一种简单的方法可以消除垃圾值?
当您使用 Python 的 Paramiko 库进行 SSH 并从远程机器的 CLI 获取输出时,是不是有一种简单的方法可以消除垃圾值?
当您使用 Python 的 Paramiko 库进行 SSH 并从远程机器的 CLI 获取输出时,是不是有一种简单的方法可以消除垃圾值?