gpt4 book ai didi

How to offset every Shape separately using DragGesture?(如何使用DragGesture分别偏移每个形状?)

转载 作者:bug小助手 更新时间:2023-10-25 15:28:46 26 4
gpt4 key购买 nike



I have an array of Star() shapes. I'm able to add new shapes to a screen with button taps. If I have just one shape, I can drag this single shape with a gesture. But if I have two or more shapes, I can only simultaneously drag all the shapes, not each shape separately from the others.

我有一个Star()形状数组。我可以通过点击按钮在屏幕上添加新的形状。如果我只有一个形状,我可以用手势拖动这个形状。但如果我有两个或多个形状,我只能同时拖动所有形状,而不是每个形状与其他形状分开。


How to drag each shape separately?

如何分别拖动每个形状?


Here's a code:

下面是一个代码:


import SwiftUI

struct ContentView: View {
@State var arrayOfStars: [any Shape] = []
@State var off = CGSize.zero
let star = Star()

var body: some View {
ZStack {
HStack {
Button {
arrayOfStars.append(star)
} label: {
Text("Add Star").foregroundColor(.red)
}
}
ForEach(arrayOfStars.indices, id: \.self) { i in
star
.frame(width: 25)
.offset(x: -100)
.offset(x: 20 + CGFloat(i * 42), y: -100)
.foregroundColor(.red)
.offset(x: off.width, y: off.height)
.gesture(
DragGesture()
.onChanged { val in
off = val.translation
}
)
}
}
}
}

更多回答
优秀答案推荐

The main thing that you need to change is to structure your code so that there are different offsets for the different stars. You have only one off state variable, so it’s really not possible to move each star in a different direction.

您需要更改的主要内容是构建代码结构,以便不同的星号有不同的偏移量。你只有一个关闭状态变量,所以真的不可能让每颗恒星朝不同的方向移动。


There are many ways you could reorganize your code to achieve per-star offsetting, but the way I’m going to suggest is that you encapsulate each star, along with its own individual off state variable, into a separate View, which I will call StarView. Then with minimal other changes, you get something like this:

有很多方法可以重新组织代码以实现每个恒星的偏移,但我建议的方法是将每个恒星以及它自己的单独OFF状态变量封装到一个单独的视图中,我将其称为Starview。然后,通过最少的其他更改,您会得到如下所示:


struct StarView: View {
@State private var off = CGSize()

var body: some View {
Star()
.offset(x: off.width, y: off.height)
.gesture(
DragGesture()
.onChanged { val in
off = val.translation
}
)
}
}

struct ContentView: View {
@State var arrayOfStars: [StarView] = []

var body: some View {
ZStack {
HStack {
Button {
arrayOfStars.append(StarView())
} label: {
Text("Add Star").foregroundColor(.red)
}
}

ForEach(arrayOfStars.indices, id: \.self) { i in
arrayOfStars[i]
.frame(width: 25)
.offset(x: -100)
.offset(x: 20 + CGFloat(i * 42), y: -100)
.foregroundColor(.red)
}
}
}
}

I hope that helps!

我希望这会有帮助!


P.S. There are still some issues relating to how you deal with drag gestures (onChanged vs onEnded), but that’s outside the scope of your question. It should be easy for you to figure that out, just by experimenting with a single StarView.

附注:关于如何处理拖拽手势(onChanged和onEnded),仍有一些问题,但这超出了你的问题范围。你应该很容易弄清楚这一点,只需尝试一下单一的星景即可。



I would suggest you think of a star as a view of an offset. To make it possible to add multiple autonomous stars:

我建议你把一颗星想象成一种补偿。若要添加多颗自主恒星,请执行以下操作:



  • every star needs its own separate state;

  • in order for that state to remain under the control of the parent container, I would suggest encapsulating it in an ObservableObject;

  • the ObservableObject are owned by the container but viewed and updated by the individual stars;

  • in order for it to be possible to drag a star more than once, you need to record where it was previously dragged to.


Here is an updated version of your code to show it working. Since I didn't have a star shape to hand, I used an image instead.

以下是您的代码的更新版本,以显示其工作情况。因为我手头上没有星形,所以我使用了一个图像。


class StarOffset: ObservableObject, Identifiable {
let id: Int
@Published private var offset: CGSize
private var previousOffset: CGSize

init(id: Int, initialOffset: CGSize) {
self.id = id
self.offset = initialOffset
self.previousOffset = initialOffset
}

var currentOffset: CGSize {
offset
}

func updateOffset(translation: CGSize) {
offset = CGSize(
width: previousOffset.width + translation.width,
height: previousOffset.height + translation.height
)
}

func saveOffset() {
previousOffset = offset
}
}

struct Star: View {

@ObservedObject var offset: StarOffset

var body: some View {
Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.frame(width: 25)
.foregroundColor(.red)
.offset(offset.currentOffset)
.gesture(
DragGesture()
.onChanged { val in
offset.updateOffset(translation: val.translation)
}
.onEnded { _ in
offset.saveOffset()
}
)
}
}

struct ContentView: View {
@State var starOffsets = [StarOffset]()

private var nextStarOffset: StarOffset {
let i = starOffsets.count
return StarOffset(
id: i,
initialOffset: CGSize(
width: CGFloat(i * 42) - 80,
height: -100
)
)
}

var body: some View {
ZStack {
HStack {
Button {
starOffsets.append(nextStarOffset)
} label: {
Text("Add Star").foregroundColor(.red)
}
}
ForEach(starOffsets) { offset in
Star(offset: offset)
}
}
}
}


You should associate an offset to each star. You can make a struct like this:

您应该为每个星形关联一个偏移量。您可以创建这样的结构:


struct OffsetShape: Identifiable, Hashable {

let shape: AnyShape
var offset: CGSize
let id = UUID()

static func == (lhs: OffsetShape, rhs: OffsetShape) -> Bool {
lhs.id == rhs.id && lhs.offset == rhs.offset
}

func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(offset.width)
hasher.combine(offset.height)
}
}

@State var arrayOfStars: [OffsetShape] = []

Button {
let i = arrayOfStars.count - 1
arrayOfStars.append(
OffsetShape(shape: AnyShape(star), offset: .init(width: 20 + CGFloat(i) * 42, height: -100))
)
} label: {
Text("Add Star").foregroundColor(.red)
}

ForEach($arrayOfStars) { shapeAndOffset in
// get/set shapeAndOffset.shape.wrappedValue
// and shapeAndOffset.offset.wrappedValue instead
}

Your drag gesture has a problem though. Once you start dragging, the shape snaps back to its initial position. This is because val.translation is relative to the drag start location, not relative to the offset of the shape when the dragging started.

不过,你的拖拽手势有一个问题。开始拖动后,形状将捕捉回其初始位置。这是因为val.平移相对于拖动开始位置,而不是相对于开始拖动时形状的偏移量。


To make the dragging look better, you should also track the starting offset. Update the start offset when each drag gesture ends. See also a more complete guide here.

若要使拖动效果更好,还应跟踪起始偏移。在每个拖动手势结束时更新开始偏移量。另请参阅此处更完整的指南。


struct OffsetShape: Identifiable, Hashable {

let shape: AnyShape
var draggedOffset: CGSize = .zero
var startOffset: CGSize
let id = UUID()

static func == (lhs: OffsetShape, rhs: OffsetShape) -> Bool {
lhs.id == rhs.id && lhs.finalOffset == rhs.finalOffset
}

func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(finalOffset.width)
hasher.combine(finalOffset.height)
}

var finalOffset: CGSize {
.init(width: startOffset.width + draggedOffset.width, height: startOffset.height + draggedOffset.height)
}
}

struct ContentView: View {
@State var arrayOfStars: [OffsetShape] = []
let star = Star()

var body: some View {
ZStack {
HStack {
Button {
let i = arrayOfStars.count - 1
arrayOfStars.append(
OffsetShape(shape: AnyShape(star), startOffset: .init(width: 20 + CGFloat(i) * 42, height: -100))
)
} label: {
Text("Add Star").foregroundColor(.red)
}
}
ForEach($arrayOfStars) { shapeAndOffset in
shapeAndOffset.wrappedValue.shape
.frame(width: 25)
.foregroundColor(.red)
.offset(x: shapeAndOffset.wrappedValue.finalOffset.width, y: shapeAndOffset.wrappedValue.finalOffset.height)
.gesture(
DragGesture()
.onChanged { val in
shapeAndOffset.draggedOffset.wrappedValue = val.translation
}
.onEnded({ val in
shapeAndOffset.startOffset.width.wrappedValue += val.translation.width
shapeAndOffset.startOffset.height.wrappedValue += val.translation.height
shapeAndOffset.draggedOffset.wrappedValue = .zero
})
)
}
}
}
}

更多回答

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com