簡體   English   中英

更改 SwiftUI DragGesture 中的數組順序

[英]Change array sequence in SwiftUI DragGesture

我目前正在嘗試構建一副卡片來代表用戶卡片。

這是我的第一個原型,請忽略樣式問題。 我希望用戶能夠更改卡片的順序,我試圖通過拖動手勢來實現。

卡疊

我的想法是,當拖動手勢的平移大於卡片偏移量(或小於卡片負偏移量)時,我只需交換數組中的兩個元素即可交換視圖中的卡片(以及 viewModel 因為綁定)。 不幸的是,這不起作用,我不知道為什么。 這是我的觀點:

struct CardHand: View {
    @Binding var cards: [Card]
    @State var activeCardIndex: Int?
    @State var activeCardOffset: CGSize = CGSize.zero
    
    var body: some View {
        ZStack {
            ForEach(cards.indices) { index in
                GameCard(card: cards[index])
                    .offset(x: CGFloat(-30 * index), y: self.activeCardIndex == index ? -20 : 0)
                    .offset(activeCardIndex == index ? activeCardOffset : CGSize.zero)
                    .rotationEffect(Angle.degrees(Double(-2 * Double(index - cards.count))))
                    .zIndex(activeCardIndex == index ? 100 : Double(index))
                    .gesture(getDragGesture(for: index))
            }
            // Move the stack back to the center of the screen
            .offset(x: CGFloat(12 * cards.count), y: 0)
        }
    }
    
    private func getDragGesture(for index: Int) -> some Gesture {
        DragGesture()
            .onChanged { gesture in
                self.activeCardIndex = index
                self.activeCardOffset = gesture.translation
            }
            .onEnded { gesture in
                if gesture.translation.width > 30, index < cards.count {
                    cards.swapAt(index, index + 1)
                }
                self.activeCardIndex = nil
                // DEBUG: Try removing the card after drag gesture ended. Not working either.
                cards.remove(at: index)
            }
    }
}

游戲卡:

struct GameCard: View {
    let card: Card
    var symbol: (String, Color) {
        switch card.suit.name {
            case "diamonds":
                return ("♦", .red)
            case "hearts":
                return ("♥", .red)
            case "spades":
                return ("♠", .black)
            case "clubs":
                return ("♣", .black)
            default:
                return ("none", .black)
        }
    }
    
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 25)
                .fill(Color.white)
                .addBorder(Color.black, width: 3, cornerRadius: 25)
            VStack {
                Text(self.card.rank.type.description())
                Text(self.symbol.0)
                    .font(.system(size: 100))
                    .foregroundColor(self.symbol.1)
            }
        }
        .frame(minWidth: 20, idealWidth: 80, maxWidth: 80, minHeight: 100, idealHeight: 100, maxHeight: 100, alignment: .center)
    }
}

后面的Model:

public struct Card: Identifiable, Hashable {
    public var id: UUID = UUID()
    public var suit: Suit
    public var rank: Rank
}

public enum Type: Identifiable, Hashable {
    case face(String)
    case numeric(rankNumber: Int)
    
    public var id: Type { self }
    
    public func description() -> String{
        switch self {
        case .face(let description):
            return description
        case .numeric(let rankNumber):
            return String(rankNumber)
        }
    }
}

public struct Rank: RankProtocol, Identifiable, Hashable {
    public var id: UUID = UUID()
    public var type: Type
    public var value: Int
    public var ranking: Int
}

public struct Suit: SuitProtocol, Identifiable, Hashable {
    public var id: UUID = UUID()
    public var name: String
    public var value: Int
    
    public init(name: String, value: Int) {
        self.name = name
        self.value = value
    }
}

public protocol RankProtocol {
    var type: Type { get }
    var value: Int { get }
}

public protocol SuitProtocol {
    var name: String { get }
}

誰能告訴我為什么這沒有按我的預期工作? 我錯過了什么基本的東西嗎?

謝謝!

這里發生了很多小錯誤。 這是一個(粗略的)解決方案,我將在下面詳細說明:


struct CardHand: View {
    @Binding var cards: [Card]
    @State var activeCardIndex: Int?
    @State var activeCardOffset: CGSize = CGSize.zero
    
    func offsetForCardIndex(index: Int) -> CGSize {
        var initialOffset = CGSize(width: CGFloat(-30 * index), height: 0)
        guard index == activeCardIndex else {
            return initialOffset
        }
        initialOffset.width += activeCardOffset.width
        initialOffset.height += activeCardOffset.height
        return initialOffset
    }
    
    var body: some View {
        ZStack {
            ForEach(cards.indices) { index in
                GameCard(card: cards[index])
                    .offset(offsetForCardIndex(index: index))
                    .rotationEffect(Angle.degrees(Double(-2 * Double(index - cards.count))))
                    .zIndex(activeCardIndex == index ? 100 : Double(index))
                    .gesture(getDragGesture(for: index))
            }
            // Move the stack back to the center of the screen
            .offset(x: CGFloat(12 * cards.count), y: 0)
        }
    }
    
    private func getDragGesture(for index: Int) -> some Gesture {
        DragGesture(minimumDistance: 0, coordinateSpace: .local)
            .onChanged { gesture in
                self.activeCardIndex = index
                self.activeCardOffset = gesture.translation
            }
            .onEnded { gesture in
                if gesture.translation.width > 30 {
                    if index - 1 > 0 {
                        cards.swapAt(index, index - 1)
                    }
                }
                if gesture.translation.width < -30 {
                    cards.swapAt(index, index + 1)
                }
                self.activeCardIndex = nil
            }
    }
}
  1. 嘗試連續進行兩次offset()調用是有問題的。 我將兩者組合成offsetForCardIndex
  2. 因為您正在執行ZStack ,請記住cards數組中的第一項將位於堆棧的底部並且朝向屏幕的右側。 這影響了后來的邏輯
  3. 您需要確保在swapAt中檢查數組的邊界,以免最終嘗試交換超出索引(這是之前發生的情況)

我的“解決方案”仍然很粗糙——應該進行更多檢查以確保陣列不會 go 越界。 此外,在您的原始版本和我的版本中,從用戶體驗的角度來看,能夠交換超過 1 個 position 可能更有意義。 但是,在這個問題的 scope 之外,這是一個更大的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM