在 SwiftUI 中将拖动手势添加到 ForEach 中的图像
Adding Drag Gesture to image inside a ForEach in SwiftUI
我目前正在开发一个应用程序,向用户显示他的植物箱和一些测量值(如地面湿度、温度等)
每个植物箱内都有一些植物,它们以详细视图显示。我希望用户能够将其中一株植物拖到一个角落以将其移除。
目前我有这个实现:
@GestureState private var dragOffset = CGSize.zero
var body: some View {
//Plants Headline
VStack (spacing: 5) {
Text("Plants")
.font(.headline)
.fontWeight(.light)
//HStack for Plants Images
HStack (spacing: 10) {
//For each that loops through all of the plants inside the plant box
ForEach(plantBoxViewModel.plants) { plant in
//Image of the individual plant (obtained via http request to the backend)
Image(base64String: plant.picture)
.resizable()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.offset(x: self.dragOffset.width, y: self.dragOffset.height)
.animation(.easeInOut)
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
state = value.translation
})
)
}
}
}
}
看起来像下面这样:
Screenshot of Code Snippet
但是,当我开始拖动时,两个图像都移动了。我认为这是因为两个图像的偏移量都绑定到同一个变量 (DragOffset)。我怎样才能做到只有一张图片在移动?
我想过实现某种数组,将植物的索引映射到特定的偏移量,但我不知道如何将其实现到手势中。
感谢您的帮助!
这是可能的方法 - 在拖动过程中保持选择。使用 Xcode 11.4 / iOS 13.4.
测试
@GestureState private var dragOffset = CGSize.zero
@State private var selected: Plant? = nil // << your plant type here
var body: some View {
//Plants Headline
VStack (spacing: 5) {
Text("Plants")
.font(.headline)
.fontWeight(.light)
//HStack for Plants Images
HStack (spacing: 10) {
//For each that loops through all of the plants inside the plant box
ForEach(plantBoxViewModel.plants) { plant in
//Image of the individual plant (obtained via http request to the backend)
Image(base64String: plant.picture)
.resizable()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.offset(x: self.selected == plant ? self.dragOffset.width : 0,
y: self.selected == plant ? self.dragOffset.height : 0)
.animation(.easeInOut)
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
if nil == self.selected {
self.selected = plant
}
state = value.translation
}).onEnded { _ in self.selected = nil }
)
}
}
}
}
@Asperi 的回答完成了工作,但我必须为 Xcode 13 更新它,这样它就不会抛出“在视图更新期间修改状态,这将导致未定义的行为”。
基本上,.updating
修饰符会不断更新 @state var selected
,这会导致整个视图刷新,而当我们试图影响它的新值时,它会抛出未定义的行为。
.gesture(
DragGesture()
.onChanged { value in
// Updated
if nil == self.selected {
self.selected = plant
}
}
.updating(self.$dragOffset, body: { (value, state, transaction) in
// This part is removed for causing undefined behavior
// if nil == self.selected {
// self.selected = plant
// }
state = value.translation
}).onEnded { _ in self.selected = nil }
)
您可以在此处阅读更多相关信息:Modifying state during view update
我目前正在开发一个应用程序,向用户显示他的植物箱和一些测量值(如地面湿度、温度等) 每个植物箱内都有一些植物,它们以详细视图显示。我希望用户能够将其中一株植物拖到一个角落以将其移除。 目前我有这个实现:
@GestureState private var dragOffset = CGSize.zero
var body: some View {
//Plants Headline
VStack (spacing: 5) {
Text("Plants")
.font(.headline)
.fontWeight(.light)
//HStack for Plants Images
HStack (spacing: 10) {
//For each that loops through all of the plants inside the plant box
ForEach(plantBoxViewModel.plants) { plant in
//Image of the individual plant (obtained via http request to the backend)
Image(base64String: plant.picture)
.resizable()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.offset(x: self.dragOffset.width, y: self.dragOffset.height)
.animation(.easeInOut)
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
state = value.translation
})
)
}
}
}
}
看起来像下面这样: Screenshot of Code Snippet
但是,当我开始拖动时,两个图像都移动了。我认为这是因为两个图像的偏移量都绑定到同一个变量 (DragOffset)。我怎样才能做到只有一张图片在移动?
我想过实现某种数组,将植物的索引映射到特定的偏移量,但我不知道如何将其实现到手势中。 感谢您的帮助!
这是可能的方法 - 在拖动过程中保持选择。使用 Xcode 11.4 / iOS 13.4.
测试@GestureState private var dragOffset = CGSize.zero
@State private var selected: Plant? = nil // << your plant type here
var body: some View {
//Plants Headline
VStack (spacing: 5) {
Text("Plants")
.font(.headline)
.fontWeight(.light)
//HStack for Plants Images
HStack (spacing: 10) {
//For each that loops through all of the plants inside the plant box
ForEach(plantBoxViewModel.plants) { plant in
//Image of the individual plant (obtained via http request to the backend)
Image(base64String: plant.picture)
.resizable()
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 2))
.offset(x: self.selected == plant ? self.dragOffset.width : 0,
y: self.selected == plant ? self.dragOffset.height : 0)
.animation(.easeInOut)
.aspectRatio(contentMode: .fill)
.frame(width: 70, height: 70)
.gesture(
DragGesture()
.updating(self.$dragOffset, body: { (value, state, transaction) in
if nil == self.selected {
self.selected = plant
}
state = value.translation
}).onEnded { _ in self.selected = nil }
)
}
}
}
}
@Asperi 的回答完成了工作,但我必须为 Xcode 13 更新它,这样它就不会抛出“在视图更新期间修改状态,这将导致未定义的行为”。
基本上,.updating
修饰符会不断更新 @state var selected
,这会导致整个视图刷新,而当我们试图影响它的新值时,它会抛出未定义的行为。
.gesture(
DragGesture()
.onChanged { value in
// Updated
if nil == self.selected {
self.selected = plant
}
}
.updating(self.$dragOffset, body: { (value, state, transaction) in
// This part is removed for causing undefined behavior
// if nil == self.selected {
// self.selected = plant
// }
state = value.translation
}).onEnded { _ in self.selected = nil }
)
您可以在此处阅读更多相关信息:Modifying state during view update