首页 > 解决方案 > 仅拖动和缩放“选定”视图

问题描述

完成斯坦福 cs193p 课程 (iOS SwiftUI) 的一项任务,并且难以将拖动和缩放手势应用于仅选定的视图 (emojis)。选定的表情符号存储在 selectedEmojis 集中。当前设置如下:在我们拥有的 struct EmojiArtDocumentView 中(我只留下了相关代码):

import SwiftUI

struct EmojiArtDocumentView: View {
    @ObservedObject var document: EmojiArtDocument
    
    @State var selectedEmojis: Set<EmojiArtModel.Emoji> = []
    
    var body: some View {
        VStack(spacing: 0) {
            documentBody
            palette
        }
    }
    
    var documentBody: some View {
        GeometryReader { geometry in
            ZStack {
                Color.white.overlay(
                    OptionalImage(uiImage: document.backgroundImage)
                        .scaleEffect(zoomScale)
                        .position(convertFromEmojiCoordinates((0,0), in: geometry))
                )
                .gesture(doubleTapToZoom(in: geometry.size))
                
                    ForEach(document.emojis) { emoji in
                        Text(emoji.text)
                            .font(.system(size: fontSize(for: emoji)))
                            .scaleEffect(zoomScale)
                            .position(position(for: emoji, in: geometry))
                            .saturation(selectedEmojis.contains(emoji) ? 0 : 1)
                            .onTapGesture {
                                select(emoji)
                            }
                            .gesture(selectedEmojis.contains(emoji) ? panGesture().simultaneously(with: zoomGesture()) : nil)
                    }
            } 
        }
    }

// 下面我们实现了缩放和平移(拖动):

@State private var steadyStateZoomScale: CGFloat = 1  //document's zoom scale when not zooming with pinch
@GestureState private var gestureZoomScale: CGFloat = 1

private var zoomScale: CGFloat {
    steadyStateZoomScale * gestureZoomScale
}

private func zoomGesture() -> some Gesture {
    MagnificationGesture()
        .updating($gestureZoomScale) { latestGestureScale, gestureZoomScale, _ in
            gestureZoomScale = latestGestureScale
        }
        .onEnded { gestureScaleAtEnd in
            //at the end sets the new value to steadyStateZoomScale
            steadyStateZoomScale *= gestureScaleAtEnd
        }
}

private func doubleTapToZoom(in size: CGSize) -> some Gesture {
    TapGesture(count: 2)
        .onEnded {
            withAnimation {
                zoomToFit(document.backgroundImage, in: size)
            }
        }
}

private func zoomToFit(_ image: UIImage?, in size: CGSize) {
    if let image = image, image.size.width > 0, image.size.height > 0, size.width > 0, size.height > 0  {
        let hZoom = size.width / image.size.width
        let vZoom = size.height / image.size.height
        steadyStatePanOffset = .zero
        steadyStateZoomScale = min(hZoom, vZoom)
    }
}

// MARK: - Panning

@State private var steadyStatePanOffset: CGSize = CGSize.zero
@GestureState private var gesturePanOffset: CGSize = CGSize.zero

private var panOffset: CGSize {
    // + made via extension CGSize
    (steadyStatePanOffset + gesturePanOffset) * zoomScale
}

private func panGesture() -> some Gesture {
    DragGesture()
        .updating($gesturePanOffset) { latestDragGestureValue, gesturePanOffset, _ in
            gesturePanOffset = latestDragGestureValue.translation / zoomScale
        }
        .onEnded { finalDragGestureValue in
            steadyStatePanOffset = steadyStatePanOffset + (finalDragGestureValue.translation / zoomScale)
        }
}

如您所见,我尝试将条件设置为视图修饰符: .gesture(selectedEmojis.contains(emoji) ? panGesture().simultaneously(with: zoomGesture()) : nil) 但结果是我可以移动整个如果我触摸“已选择”,则选择,如果我触摸“未选择”,则不会移动。不知道如何仅使“选定”的表情符号移动。如果有人分享如何实现它的想法,将不胜感激。似乎不需要使@GestureState private vargesturePanOffset var 成为每个表情符号视图一个 vars 的数组...

标签: iosswiftswiftuigesturecs193p

解决方案


推荐阅读