如何在给定 15 秒间隔内捕获的坐标数组的情况下绘制和更新形状(SWIFT UI)
Posted
技术标签:
【中文标题】如何在给定 15 秒间隔内捕获的坐标数组的情况下绘制和更新形状(SWIFT UI)【英文标题】:How to draw and update a shape given an array of coordinates captured over a 15 second interval (SWIFT UI) 【发布时间】:2021-12-27 23:26:07 【问题描述】:(Swift 新手)我有一个在 15 秒内从视频中提取的单个对象的 x 坐标数组。我想遍历这些点,绘制当前点,然后刷新视图并绘制数组中的下一个坐标点,替换前一个。我还使用 swift ui 的 VideoPlayer 在绘图下方同时播放视频。
我有一个形状结构:
struct Stick: Shape
var points: [CGPoint]
var size: CGSize
func path(in rect: CGRect) -> Path
var path = Path()
path.move(to: points[0])
for point in points
path.addLine(to: point)
return path.applying(CGAffineTransform.identity.scaledBy(x: size.width, y: size.height))
.applying(CGAffineTransform(scaleX: -1, y: -1).translatedBy(x: -size.width, y: -size.height))
为了遍历每个数组中的点,我创建了一个符合 Observable Object 协议的类。
class Points : ObservableObject
var pointArr = [0.1981825828552246, 0.3635875880718231, 0.363417387008667, 0.36394527554512024, 0.3631347119808197, 0.3628629148006439, 0.36222490668296814]
@Published var point = 0.0
func updatePoint()
DispatchQueue.main.async
for p in self.pointArr
self.point = p
print(self.point)
然后我有这段代码,我使用按钮单击调用 updatePoint() 并开始遍历坐标数组。
struct VideoView: View
@ObservedObject var updatePoints : Points
var body: some View
ZStack
Button("Press")
updatePoints.updatePoint()
//I just used random fixed points for the other coordinates
Stick(points: [CGPoint(x: updatePoints.point, y: 1.0), CGPoint(x:0.36, y:0.0)], size: CGSize(width: 414, height: 814))
.stroke(lineWidth: 5.0)
.fill(Color.green)
我在这段代码背后的思考过程是,因为 Points 类符合 observable object 协议,所以每次在 for 循环中重新分配 point 变量时,都会重新绘制 VideoView,并将新的 point 值传入 Stick 结构。但是,VideoView 仅在 for 循环完成后重绘一次,将数组中的最后一个坐标传递给 Stick 结构。我认为这与主线程中的 for 循环有关,并且在 for 循环完成之前视图不会更新。
这就是我在伪代码中所追求的:
for point in videoPoints
Stick(point) //draw that point
这个问题让我困扰了几天,因此非常感谢任何帮助。谢谢!!!
【问题讨论】:
代码无法编译,主要是pointArr
。你能创建一个minimal reproducible example吗?
@George 我想我修好了,现在试试吧!
我认为问题不在于视频只是在最后一点重绘。我认为它每次都在重新绘制,直到最后一点你才能看到它。因此,您看到的不是一堆Sticks
,而是最后一个。在我看来,您可能希望一次将整个数组传递给您的视图,然后使用 ForEach 在正文中对其进行迭代。这将在一个视图中绘制多个Sticks
。
@Yrb 非常感谢您的建议!但是,当我使用 ForEach 循环时,它只会将每个“Sticks”重叠在一起。我想一次只显示一个“棒”。
您想如何确定下一个Stick
的显示时间?我仍然认为它应该在 UI 中处理,而不是在模型中。
【参考方案1】:
您可以使用组合框架随着时间的推移发布这些值。
在Points
类中,添加以下内容(替换updatePoint()
):
private var cancellables: Set<AnyCancellable> = []
func updatePoint()
let timer = Timer.publish(every: 0.2, on: .main, in: .default).autoconnect()
Publishers.Zip(pointArr.publisher, timer)
.sink (p, _) in
self.point = p
.store(in: &cancellables)
请参阅this 答案,了解延迟值如何与Timer
一起使用。基本上,Zip
仅在 pointArr.publisher
和 timer
都发出一个值时发出一个值。 pointArr
是即时的,所以 timer
可以跟上节奏。
您可以随意设置延迟,目前为0.2
秒。尽管看起来这条线可能会立即到达最后一个点,但这是因为您的采样点在第一个点之后非常靠近。试试像0.5
这样的另一个点,你会看到它仍然在移动之间有延迟。
【讨论】:
效果很好,非常感谢!以上是关于如何在给定 15 秒间隔内捕获的坐标数组的情况下绘制和更新形状(SWIFT UI)的主要内容,如果未能解决你的问题,请参考以下文章
如何设置 PyQt5 Qtimer 在指定的时间间隔内更新?
如何使用Android中的Camera2 API在不预览的情况下拍摄多张照片?