swiftui - 是否可以从视图绘制线到另一个视图
问题描述
目标示例
- 用户点击第一个圆圈按钮和第二个圆圈按钮
- 然后这条线将从第一个到第二个
- 注意:按钮是可移动的
尝试了什么
- 里面有两个
Heart
。BoardView
每颗心都有MovableCard
- 什么时候
Heart
被点击,然后通知并传递自我
struct Heart: View {
var body: some View {
MovableCard("heart.fill")
.onTapGesture {
// Notify this card is tapped
NotificationCenter.default.post(heartOnTap, ["self":self])
}
}
}
- BoardView 中有 viewModel 可以观察
heartOnTap
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))
}
}
}
}
ViewModel
将监听heartOnTap
接收Heart: View
- 我
Heart: View
不发送水龙头位置,因为它是可移动的。 - 但是
Heart
,conformedView
没有任何访问权限来直接获得它的位置
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
}
}
}
讨论
是否有从视图到另一个视图绘制线的可能解决方案。(视图可移动)
替代目标
解决方案
关于您的初始计划可能存在问题的第一件事是您试图存储对View
s 的引用。一般来说,这不应该在 SwiftUI 中完成,因为View
s 意味着是瞬态的。相反,我建议创建一个可识别的模型,您可以使用它来跟踪每个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
}
}
}
推荐阅读
- angular - 如何在 Angular 5 mat-table 中拼接数据?
- sql-server - 虚拟机之间无法连接sql server
- javascript - JS 错误:变量未定义....但它是
- javascript - 具有可比父母的 D3 包装布局
- java - 使用 selenium 在浏览器中打开多个 url
- react-native - 带有博览会和反应导航的透明状态栏
- javascript - 在引导 javascript 中禁用功能
- python - 具有内部节点值的树结构,__init__ 方法的参数
- php - 我的模态对话框表单未使用 ajax 提交到 php 中的控制器
- android - Visual Studio 2017 - 将 C++ 共享库与 Android 中的 C++ 库链接