SwiftUI 在决定最终位置的视图上同时使用 .offset 和 .position 修饰符时究竟会发生什么?
SwiftUI What exactly happens when use .offset and .position Modifier simultaneously on a View, which decides the final location?
当我尝试给View一个初始位置时,我有这个问题,然后用户可以使用拖动手势将View的位置更改到任何地方。虽然我已经通过仅在视图上使用 .position(x:y:)
解决了这个问题,但一开始我在考虑使用 .position(x:y:)
来给出初始位置,并使用 .offset(offset:)
来同时使视图随手势移动。现在,我真的只想更详细地了解,当我同时使用它们(下面的代码)时到底发生了什么,所以我可以解释下面的视图中发生了什么。
我无法在下面的视图中解释的是:当我简单地在 VStack 框上拖动手势时,它按预期工作并且 VStack 随手指手势移动,但是,一旦手势结束并尝试开始新的在 VStack 上拖动手势,VStack 框突然回到原来的位置(就像代码加载时跳转到原来的位置),然后随着手势开始移动。请注意,手势像常规手势一样移动,但 VStack 已经跳到不同的位置,因此它从不同的位置开始移动。这导致指尖不再位于 VStack 框的顶部,而是离开了一段距离,尽管 VStack 以与拖动手势相同的轨迹移动。
我的问题是:为什么 .position(x:y:)
修饰符似乎只在检测到的每个新拖动手势的最开始时生效,但在拖动手势动作期间似乎 .offset(offset:)
支配主要运动VStack 停在它被拖到的地方。但是一旦打开新的拖动手势,VStack 就会突然跳到原来的位置。我只是无法理解这种行为是如何通过时间线发生的。有人可以提供一些见解吗?
请注意,我已经解决了这个问题来实现我所需要的,现在只是为了了解当 .position(x:y:)
和 .offset(offset:)
同时使用时到底发生了什么,所以请避免一些建议,例如。不要同时使用它们,谢谢。下面的代码假设在复制和粘贴后可以运行,如果不是,请原谅我的错误,因为我删除了几行以使其更清晰地重现问题。
import SwiftUI
struct ContentView: View {
var body: some View {
ButtonsViewOffset()
}
}
struct ButtonsViewOffset: View {
let location: CGPoint = CGPoint(x: 50, y: 50)
@State private var offset = CGSize.zero
@State private var color = Color.purple
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
self.offset = value.translation
print("offset onChange: \(offset)")
}
.onEnded{ _ in
if self.color == Color.purple{
self.color = Color.blue
}
else{
self.color = Color.purple
}
}
}
var body: some View {
VStack {
Text("Watch 3-1")
Text("x: \(self.location.x), y: \(self.location.y)")
}
.background(Color.gray)
.foregroundColor(self.color)
.offset(self.offset)
.position(x: self.location.x, y: self.location.y)
.gesture(dragGesture)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
}
}
}
您的问题与position和offset的使用无关。他们实际上同时工作。 Position 设置视图的绝对位置,其中 offset 相对于绝对位置移动它。因此,您会注意到您的视图从屏幕上的位置 (50, 50) 开始,然后您可以将它拖动到各处。一旦放手,它就停在原处。到目前为止,一切都很好。然后你想再次移动它,它会弹回原来的位置。这样做的原因是您将 location 设置为 let 常量的方式。它需要状态。
问题源于您在没有意识到的情况下将偏移值添加到位置。完成拖动后,offset 会保留最后的值。但是,当您开始下一次拖动时,这些值再次从 (0,0) 开始,因此偏移量将重置为 (0,0) 并且视图将移回原始位置。关键是你需要只使用位置或更新 .onEnded
中的偏移量。不要同时使用两者。这里你有一个设定的位置,并没有保存偏移量。您如何处理它取决于您移动视图的目的。
首先,使用.position()
:
struct OffsetAndPositionView: View {
@State private var position = CGPoint(x: 50, y: 50)
@State private var color = Color.purple
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
position = value.location
print("position onChange: \(position)")
}
.onEnded{ value in
if color == Color.purple{
color = Color.blue
}
else{
color = Color.purple
}
}
}
var body: some View {
Rectangle()
.fill(color)
.frame(width: 30, height: 30)
.position(position)
.gesture(dragGesture)
}
}
其次,直接使用.offset()
:
struct ButtonsViewOffset: View {
@State private var savedOffset = CGSize.zero
@State private var dragValue = CGSize.zero
@State private var color = Color.purple
var offset: CGSize {
savedOffset + dragValue
}
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
dragValue = value.translation
print("dragValue onChange: \(dragValue)")
}
.onEnded{ value in
savedOffset = savedOffset + value.translation
dragValue = CGSize.zero
if color == Color.purple{
color = Color.blue
}
else{
color = Color.purple
}
}
}
var body: some View {
Rectangle()
.fill(color)
.frame(width: 30, height: 30)
.offset(offset)
.gesture(dragGesture)
}
}
// Convenience operator overload
func + (lhs: CGSize, rhs: CGSize) -> CGSize {
return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
}
当我尝试给View一个初始位置时,我有这个问题,然后用户可以使用拖动手势将View的位置更改到任何地方。虽然我已经通过仅在视图上使用 .position(x:y:)
解决了这个问题,但一开始我在考虑使用 .position(x:y:)
来给出初始位置,并使用 .offset(offset:)
来同时使视图随手势移动。现在,我真的只想更详细地了解,当我同时使用它们(下面的代码)时到底发生了什么,所以我可以解释下面的视图中发生了什么。
我无法在下面的视图中解释的是:当我简单地在 VStack 框上拖动手势时,它按预期工作并且 VStack 随手指手势移动,但是,一旦手势结束并尝试开始新的在 VStack 上拖动手势,VStack 框突然回到原来的位置(就像代码加载时跳转到原来的位置),然后随着手势开始移动。请注意,手势像常规手势一样移动,但 VStack 已经跳到不同的位置,因此它从不同的位置开始移动。这导致指尖不再位于 VStack 框的顶部,而是离开了一段距离,尽管 VStack 以与拖动手势相同的轨迹移动。
我的问题是:为什么 .position(x:y:)
修饰符似乎只在检测到的每个新拖动手势的最开始时生效,但在拖动手势动作期间似乎 .offset(offset:)
支配主要运动VStack 停在它被拖到的地方。但是一旦打开新的拖动手势,VStack 就会突然跳到原来的位置。我只是无法理解这种行为是如何通过时间线发生的。有人可以提供一些见解吗?
请注意,我已经解决了这个问题来实现我所需要的,现在只是为了了解当 .position(x:y:)
和 .offset(offset:)
同时使用时到底发生了什么,所以请避免一些建议,例如。不要同时使用它们,谢谢。下面的代码假设在复制和粘贴后可以运行,如果不是,请原谅我的错误,因为我删除了几行以使其更清晰地重现问题。
import SwiftUI
struct ContentView: View {
var body: some View {
ButtonsViewOffset()
}
}
struct ButtonsViewOffset: View {
let location: CGPoint = CGPoint(x: 50, y: 50)
@State private var offset = CGSize.zero
@State private var color = Color.purple
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
self.offset = value.translation
print("offset onChange: \(offset)")
}
.onEnded{ _ in
if self.color == Color.purple{
self.color = Color.blue
}
else{
self.color = Color.purple
}
}
}
var body: some View {
VStack {
Text("Watch 3-1")
Text("x: \(self.location.x), y: \(self.location.y)")
}
.background(Color.gray)
.foregroundColor(self.color)
.offset(self.offset)
.position(x: self.location.x, y: self.location.y)
.gesture(dragGesture)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
}
}
}
您的问题与position和offset的使用无关。他们实际上同时工作。 Position 设置视图的绝对位置,其中 offset 相对于绝对位置移动它。因此,您会注意到您的视图从屏幕上的位置 (50, 50) 开始,然后您可以将它拖动到各处。一旦放手,它就停在原处。到目前为止,一切都很好。然后你想再次移动它,它会弹回原来的位置。这样做的原因是您将 location 设置为 let 常量的方式。它需要状态。
问题源于您在没有意识到的情况下将偏移值添加到位置。完成拖动后,offset 会保留最后的值。但是,当您开始下一次拖动时,这些值再次从 (0,0) 开始,因此偏移量将重置为 (0,0) 并且视图将移回原始位置。关键是你需要只使用位置或更新 .onEnded
中的偏移量。不要同时使用两者。这里你有一个设定的位置,并没有保存偏移量。您如何处理它取决于您移动视图的目的。
首先,使用.position()
:
struct OffsetAndPositionView: View {
@State private var position = CGPoint(x: 50, y: 50)
@State private var color = Color.purple
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
position = value.location
print("position onChange: \(position)")
}
.onEnded{ value in
if color == Color.purple{
color = Color.blue
}
else{
color = Color.purple
}
}
}
var body: some View {
Rectangle()
.fill(color)
.frame(width: 30, height: 30)
.position(position)
.gesture(dragGesture)
}
}
其次,直接使用.offset()
:
struct ButtonsViewOffset: View {
@State private var savedOffset = CGSize.zero
@State private var dragValue = CGSize.zero
@State private var color = Color.purple
var offset: CGSize {
savedOffset + dragValue
}
var dragGesture: some Gesture {
DragGesture()
.onChanged{ value in
dragValue = value.translation
print("dragValue onChange: \(dragValue)")
}
.onEnded{ value in
savedOffset = savedOffset + value.translation
dragValue = CGSize.zero
if color == Color.purple{
color = Color.blue
}
else{
color = Color.purple
}
}
}
var body: some View {
Rectangle()
.fill(color)
.frame(width: 30, height: 30)
.offset(offset)
.gesture(dragGesture)
}
}
// Convenience operator overload
func + (lhs: CGSize, rhs: CGSize) -> CGSize {
return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height)
}