По сути, ваш код говорит каждому представлению следовать за смещением, в то время как на самом деле вам нужно только верхнее движение. Итак, во-первых, я бы добавил переменную, которая будет хранить текущий индекс карты, и метод для вычисления ее смещения:
@State private var currentCard = 0
func offset(for i: Int) -> CGSize {
return i == currentCard ? offset : .zero
}
Во-вторых, я обнаружил, что если мы просто оставим это так, наследующий вид касания получит смещение последнего (-1000, 0) и только затем перейдет в правильное местоположение, так что это выглядит так, как будто предыдущая карта решила вернуться вместо новой. Чтобы это исправить, я добавил флаг, отмечающий, что карта только что ушла, поэтому, когда мы снова ее трогаем, она сначала попадает в нужное место. Обычно мы делаем это в состоянии жеста .began
, но у нас нет аналога для этого в swiftUI, поэтому единственное место, где это можно сделать, находится в .onChanged
:
@State private var didJustSwipe = false
DragGesture()
.onChanged {
if self.didJustSwipe {
self.didJustSwipe = false
self.currentCard += 1
self.offset = .zero
} else {
self.offset = $0.translation
}
}
In.onEnded
в случае успеха мы присваиваем didJustSwipe = true
Так что теперь это работает отлично. Также я предлагаю вам погрузить ваш код в более мелкие части. Это не только улучшит читабельность, но и сэкономит время компиляции. Вы не предоставили реализацию EventViewModel
и тех randomViews
, поэтому я использовал вместо этого прямоугольники. Вот ваш код:
struct EventView: View {
@State private var offset: CGSize = .zero
@State private var currentCard = 0
@State private var didJustSwipe = false
var randomView: some View {
return Rectangle()
.foregroundColor(.green)
.cornerRadius(20)
.frame(width: 300, height: 400)
.shadow(radius: 10)
.padding()
.opacity(0.3)
}
func offset(for i: Int) -> CGSize {
return i == currentCard ? offset : .zero
}
var body: some View {
ZStack{
ForEach(currentCard..<5, id: \.self) { i in
self.randomView
.offset(self.offset(for: i))
.gesture(self.gesture)
.animation(.spring())
}
}
}
var gesture: some Gesture {
DragGesture()
.onChanged {
if self.didJustSwipe {
self.didJustSwipe = false
self.currentCard += 1
self.offset = .zero
} else {
self.offset = $0.translation
}
}
.onEnded {
let w = $0.translation.width
if abs(w) > 100 {
self.didJustSwipe = true
let x = w > 0 ? 1000 : -1000
self.offset = .init(width: x, height: 0)
} else {
self.offset = .zero
}
}
}
}