首页 > 解决方案 > 是否可以从视图绘制线到另一个视图

问题描述

目标示例

在此处输入图像描述

尝试了什么

struct Heart: View {
  var body: some View {
    MovableCard("heart.fill")
      .onTapGesture {
        // Notify this card is tapped
        NotificationCenter.default.post(heartOnTap, ["self":self])
      }
  }
}
struct BoardView: View {
  @StateObject var viewModel = ViewModel()
  var body: some View {
    ZStack{
      Heart()
      Heart()
      Path{ path in
        path.move(to: viewModel.firstCard.POS ?? CGPoint(x: -1, y: -1))
        path.addLine(to: viewModel.secondCard.POS ?? CGPoint(x: -1, y: -1))
      }
    }
  }
}
class ViewModel: ObservableObject {
  @Published var firstCard: Heart?
  @Published var secondCard: Heart?

  init() {
    NotificationCenter.default.addObserver(self, 
      selector: #selector(self.didTap(_:)), 
      name: heartOnTap, 
      object: nil)
  }

  @objc func didTap(_ notification: NSNotification) {
    guard let heart = notification.userInfo?["self"] as? Heart else { return }
    if firstCard == nil {
      firstCard = HEARTPOSITION // <- Heart position for drawing the line
    }
    if secondCard == nil {
      secondCard = HEARTPOSITION // <- Heart position for drawing the line
    }
  }
}

讨论

是否有从视图到另一个视图绘制线的可能解决方案。(视图可移动)

替代目标

数字模拟 (iOS)

在此处输入图像描述

标签: swiftui

解决方案


关于您的初始计划可能存在问题的第一件事是您试图存储对Views 的引用。一般来说,这不应该在 SwiftUI 中完成,因为Views 意味着是瞬态的。相反,我建议创建一个可识别的模型,您可以使用它来跟踪每个View.

可以使用将View的位置向上发送回View层次结构PreferenceKey。然后,Path可以从父视图中记录的位置绘制a。

这里有很多小细节(比如ForEach用于心脏视图、初始位置等),都可以根据您自己的情况进行更改。重要的部分是将位置向上传输,然后根据这些位置绘制路径。

struct HeartModel : Identifiable {
    var id = UUID()
}

struct HeartView : View {
    var model : HeartModel
    @State private var offset = CGSize(width: 100, height: 100)
    @State private var initialDragPosition = CGSize(width: 100, height: 100)
    
    var body: some View {
        VStack {
            Image(systemName: "heart.fill")
                .resizable()
                .frame(width: 50, height: 50)
                .position(x: offset.width, y: offset.height)
                .preference(key: ViewPositionKey.self, value: [model.id:offset])
                .gesture(
                    DragGesture()
                        .onChanged { gesture in
                            self.offset = CGSize(width: initialDragPosition.width + gesture.translation.width, height: initialDragPosition.height + gesture.translation.height)
                        }
                        .onEnded { _ in
                            initialDragPosition = self.offset
                        }
                )
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

struct ContentView : View {
    @State private var heartModels : [HeartModel] = [.init(), .init()]
    @State private var positions : [UUID:CGSize] = [:]
    
    var body: some View {
        ZStack {
            ForEach(heartModels) { heart in
                HeartView(model: heart)
            }
            
            Path { path in
                if let first = positions.first {
                    path.move(to: CGPoint(x: first.value.width, y: first.value.height))
                }
                positions.forEach { item in
                    path.addLine(to: CGPoint(x: item.value.width, y: item.value.height))
                }
            }.stroke()
            
        }.onPreferenceChange(ViewPositionKey.self) { newPositions in
            positions = newPositions
        }
    }
}

struct ViewPositionKey: PreferenceKey {
    static var defaultValue: [UUID:CGSize] { [:] }
    static func reduce(value: inout [UUID:CGSize], nextValue: () -> [UUID:CGSize]) {
        let next = nextValue()
        if let item = next.first {
            value[item.key] = item.value
        }
    }
}

在此处输入图像描述


推荐阅读