ZStackのビューの位置を別のビューの上または下にドラッグして再配置するにはどうすればよいですか(この例では、カードを別のカードの上または下にドラッグしてデッキ内のカードの順序を再配置して、ドラッグしたカードを移動するにはどうすればよいですか?)デッキ内の前記カードの後ろまたは前)。
スタック内で上または下にドラッグするとカードのインデックスが変化し、ドラッグするとスタック内のすべてのカードの後ろに滑らかに表示され、マウスを上に置いたままにします。
Summary:言い換えると、ドラッグしたカードとその上のカードは、上にドラッグすると切り替わり、ドラッグしたカードと下のカードは下にドラッグすると切り替わります。
これは、_struct CardView: View
_のZStackの順序を変更し、カードがドラッグされた量を評価することによってDragGesture().onChanged
内から位置を更新することと関係があると思います(おそらくself.offset値を表示することによって)しかし、これを確実に行う方法を見つけることができませんでした。
ここに私が今持っているものがあります:
コード:
_import SwiftUI
let cardSpace:CGFloat = 10
struct ContentView: View {
@State var cardColors: [Color] = [.orange, .green, .yellow, .purple, .red, .orange, .green, .yellow, .purple]
var body: some View {
HStack {
VStack {
CardView(colors: self.$cardColors)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.position(x: 370, y: 300)
}
}
struct CardView: View {
@State var offset = CGSize.zero
@State var dragging:Bool = false
@State var tapped:Bool = false
@State var tappedLocation:Int = -1
@Binding var colors: [Color]
@State var locationDragged:Int = -1
var body: some View {
GeometryReader { reader in
ZStack {
ForEach(0..<self.colors.count, id: \.self) { i in
ColorCard(reader:reader, i:i, colors: self.$colors, offset: self.$offset, tappedLocation: self.$tappedLocation, locationDragged:self.$locationDragged, tapped: self.$tapped, dragging: self.$dragging)
}
}
}
.animation(.spring())
}
}
struct ColorCard: View {
var reader: GeometryProxy
var i:Int
@State var offsetHeightBeforeDragStarted: Int = 0
@Binding var colors: [Color]
@Binding var offset: CGSize
@Binding var tappedLocation:Int
@Binding var locationDragged:Int
@Binding var tapped:Bool
@Binding var dragging:Bool
var body: some View {
VStack {
Group {
VStack {
self.colors[i]
}
.frame(width: 300, height: 400)
.cornerRadius(20).shadow(radius: 20)
.offset(
x: (self.locationDragged == i) ? CGFloat(i) * self.offset.width / 14
: 0,
y: (self.locationDragged == i) ? CGFloat(i) * self.offset.height / 4
: 0
)
.offset(
x: (self.tapped && self.tappedLocation != i) ? 100 : 0,
y: (self.tapped && self.tappedLocation != i) ? 0 : 0
)
.position(x: reader.size.width / 2, y: (self.tapped && self.tappedLocation == i) ? -(cardSpace * CGFloat(i)) + 0 : reader.size.height / 2)
}
.rotationEffect(
(i % 2 == 0) ? .degrees(-0.2 * Double(arc4random_uniform(15)+1) ) : .degrees(0.2 * Double(arc4random_uniform(15)+1) )
)
.onTapGesture() { //Show the card
self.tapped.toggle()
self.tappedLocation = self.i
}
.gesture(
DragGesture()
.onChanged { gesture in
self.locationDragged = self.i
self.offset = gesture.translation
self.dragging = true
}
.onEnded { _ in
self.locationDragged = -1 //Reset
self.offset = .zero
self.dragging = false
self.tapped = false //enable drag to dismiss
self.offsetHeightBeforeDragStarted = Int(self.offset.height)
}
)
}.offset(y: (cardSpace * CGFloat(i)))
}
}
_
これをチェックしてください:
「トリック」とは、アイテムのzオーダーを並べ替える必要があることです。したがって、配列内のカードを「保持」する必要があります。
let cardSpace:CGFloat = 10
struct Card : Identifiable, Hashable, Equatable {
static func == (lhs: Card, rhs: Card) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
var id = UUID()
var intID : Int
static let cardColors: [Color] = [.orange, .green, .yellow, .purple, .red, .orange, .green, .yellow, .purple]
var zIndex : Int
var color : Color
}
class Data: ObservableObject {
@Published var cards : [Card] = []
init() {
for i in 0..<Card.cardColors.count {
cards.append(Card(intID: i, zIndex: i, color: Card.cardColors[i]))
}
}
}
struct ContentView: View {
@State var data : Data = Data()
var body: some View {
HStack {
VStack {
CardView().environmentObject(data)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
// .position(x: 370, y: 300)
}
}
struct CardView: View {
@EnvironmentObject var data : Data
@State var offset = CGSize.zero
@State var dragging:Bool = false
@State var tapped:Bool = false
@State var tappedLocation:Int = -1
@State var locationDragged:Int = -1
var body: some View {
GeometryReader { reader in
ZStack {
ForEach(self.data.cards, id: \.self) { card in
ColorCard(card: card, reader:reader, offset: self.$offset, tappedLocation: self.$tappedLocation, locationDragged:self.$locationDragged, tapped: self.$tapped, dragging: self.$dragging)
.environmentObject(self.data)
.zIndex(Double(card.zIndex))
}
}
}
.animation(.spring())
}
}
struct ColorCard: View {
@EnvironmentObject var data : Data
var card: Card
var reader: GeometryProxy
@State var offsetHeightBeforeDragStarted: Int = 0
@Binding var offset: CGSize
@Binding var tappedLocation:Int
@Binding var locationDragged:Int
@Binding var tapped:Bool
@Binding var dragging:Bool
var body: some View {
VStack {
Group {
VStack {
card.color
}
.frame(width: 300, height: 400)
.cornerRadius(20).shadow(radius: 20)
.offset(
x: (self.locationDragged == card.intID) ? CGFloat(card.zIndex) * self.offset.width / 14
: 0,
y: (self.locationDragged == card.intID) ? CGFloat(card.zIndex) * self.offset.height / 4
: 0
)
.offset(
x: (self.tapped && self.tappedLocation != card.intID) ? 100 : 0,
y: (self.tapped && self.tappedLocation != card.intID) ? 0 : 0
)
.position(x: reader.size.width / 2, y: (self.tapped && self.tappedLocation == card.intID) ? -(cardSpace * CGFloat(card.zIndex)) + 0 : reader.size.height / 2)
}
.rotationEffect(
(card.zIndex % 2 == 0) ? .degrees(-0.2 * Double(arc4random_uniform(15)+1) ) : .degrees(0.2 * Double(arc4random_uniform(15)+1) )
)
.onTapGesture() { //Show the card
self.tapped.toggle()
self.tappedLocation = self.card.intID
}
.gesture(
DragGesture()
.onChanged { gesture in
self.locationDragged = self.card.intID
self.offset = gesture.translation
if self.offset.height > 60 ||
self.offset.height < -60 {
withAnimation {
if let index = self.data.cards.firstIndex(of: self.card) {
self.data.cards.remove(at: index)
self.data.cards.append(self.card)
for index in 0..<self.data.cards.count {
self.data.cards[index].zIndex = index
}
}
}
}
self.dragging = true
}
.onEnded { _ in
self.locationDragged = -1 //Reset
self.offset = .zero
self.dragging = false
self.tapped = false //enable drag to dismiss
self.offsetHeightBeforeDragStarted = Int(self.offset.height)
}
)
}.offset(y: (cardSpace * CGFloat(card.zIndex)))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(Data())
}
}
単なるアイデアです(原因は、ソリューションを再考/再コーディングする必要があるためです)。あなたのケースでの再注文はカードzIndex
の使用/変更が必要なので、どこかに保存する必要があります。
したがって、モデルとして色を直接使用する代わりに、より明示的なモデルオブジェクトが必要でした
struct Card {
var color: Color
var deckOrder: Int
}
注:以下は疑似コードであり、自分で調整する必要があります
次に、あなたはカードごとに保持して反復します(そして私はそれらを情報ObsesrvableObjectビューモデルに分けます)
ForEach(Array(vm.cards.enumerated()), id: \.element) { i, card in
ColorCard(reader:reader, i:i, cards: self.$vm.cards,
offset: self.$offset, tappedLocation: self.$tappedLocation,
locationDragged:self.$locationDragged, tapped: self.$tapped,
dragging: self.$dragging)
.zIndex(card.deckOrder)
}
変更中card.deckOrder
ドラッグすると、デッキ内のビュー/カードのzIndexが変更されます。
クリスの答えに基づいて、私はこれを思いつきました。いくつかの欠点は次のとおりです。デッキを上下に無期限に移動するには、1回のドラッグに対して1回のドラッグが必要です。
デモ:
import SwiftUI
let cardSpace:CGFloat = 10 + 20
struct Card : Identifiable, Hashable, Equatable {
static func == (lhs: Card, rhs: Card) -> Bool {
lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
var id = UUID()
var intID : Int
static let cardColors: [Color] = [.orange, .green, .yellow, .purple, .red, .orange, .green, .yellow, .purple]
var zIndex : Int
var color : Color
}
class Data: ObservableObject {
@Published var cards : [Card] = []
init() {
for i in 0..<Card.cardColors.count {
cards.append(Card(intID: i, zIndex: i, color: Card.cardColors[i]))
}
}
}
struct ContentView: View {
@State var data : Data = Data()
var body: some View {
HStack {
VStack {
CardView().environmentObject(data)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
// .position(x: 370, y: 300)
}
}
struct CardView: View {
@EnvironmentObject var data : Data
@State var offset = CGSize.zero
@State var dragging:Bool = false
@State var tapped:Bool = false
@State var tappedLocation:Int = -1
@State var locationDragged:Int = -1
var body: some View {
GeometryReader { reader in
ZStack {
ForEach((0..<self.data.cards.count), id: \.self) { i in
ColorCard(card: self.data.cards[i], reader:reader, offset: self.$offset, tappedLocation: self.$tappedLocation, locationDragged:self.$locationDragged, tapped: self.$tapped, dragging: self.$dragging, i: i)
.environmentObject(self.data)
.zIndex(Double(self.data.cards[i].zIndex))
}
}
}
.animation(.spring())
}
}
struct ColorCard: View {
@EnvironmentObject var data : Data
var card: Card
var reader: GeometryProxy
@State var offsetHeightBeforeDragStarted: Int = 0
@Binding var offset: CGSize
@Binding var tappedLocation:Int
@Binding var locationDragged:Int
@Binding var tapped:Bool
@Binding var dragging:Bool
@State var i: Int
@State var numTimesCalledSinceDragBegan: Int = 0
var body: some View {
VStack {
Group {
VStack {
card.color
}
.frame(width: 300, height: 400)
.cornerRadius(20).shadow(radius: 20)
.offset(
x: (self.numTimesCalledSinceDragBegan <= 1 && self.locationDragged == card.intID) ? CGFloat(card.zIndex) * self.offset.width / 14
: 0,
y: (self.numTimesCalledSinceDragBegan <= 1 && self.locationDragged == card.intID) ? CGFloat(card.zIndex) * self.offset.height / 4
: 0
)
.offset(
x: (self.tapped && self.tappedLocation != card.intID) ? 100 : 0,
y: (self.tapped && self.tappedLocation != card.intID) ? 0 : 0
)
.position(x: reader.size.width / 2, y: (self.tapped && self.tappedLocation == card.intID) ? -(cardSpace * CGFloat(card.zIndex)) + 0 : reader.size.height / 2)
}
// .rotationEffect(
// (card.zIndex % 2 == 0) ? .degrees(-0.2 * Double(arc4random_uniform(15)+1) ) : .degrees(0.2 * Double(arc4random_uniform(15)+1) )
// )
// .onTapGesture() { //Show the card
// self.tapped.toggle()
// self.tappedLocation = self.card.intID
//
// }
.gesture(
DragGesture()
.onChanged { gesture in
self.numTimesCalledSinceDragBegan += 1
self.locationDragged = self.card.intID
self.offset = gesture.translation
if(self.numTimesCalledSinceDragBegan == 1) {
if let index = self.data.cards.firstIndex(of: self.card) {
if(self.offset.height >= 0) {self.i += 1 } else {self.i -= 1}
self.data.cards.remove(at: index)
self.data.cards.insert(self.card, at:
(self.offset.height >= 0) ? self.i : self.i
)
for index in 0..<self.data.cards.count {
self.data.cards[index].zIndex = index
}
}
}
self.dragging = true
}
.onEnded { _ in
self.locationDragged = -1 //Reset
self.offset = .zero
self.dragging = false
self.tapped = false //enable drag to dismiss
self.offsetHeightBeforeDragStarted = Int(self.offset.height)
self.numTimesCalledSinceDragBegan = 0
}
)
}.offset(y: (cardSpace * CGFloat(card.zIndex)))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(Data())
}
}