0.4 C
New York
Friday, December 6, 2024

ios – Drag and Drop Widget The best way to make as clean as earlier than splitting into teams?


I’m presently engaged on implementing drag and drop performance for a view just like how iOS widgets behave on the house display screen. I’ve grouped views with a dimension of 1×1 in order that, when they’re shut to one another and dragged, they transfer as a single block. Nonetheless, after grouping them, the animations are not clean, or the animations appear to be lacking in comparison with earlier than.

screenshot showing layout

import SwiftUI
import UniformTypeIdentifiers

struct Card: Identifiable, Equatable {
    let id = UUID()
    let title: String
    let type: LayoutStyle
    let top: CGFloat
    
    static func ==(lhs: Card, rhs: Card) -> Bool {
        return lhs.id == rhs.id
    }
}


struct CardGroup: Identifiable {
    let id = UUID()
    let playing cards: [Card]
}


struct CardView: View {
    let title: String
    @Binding var showEdit: Bool
    var physique: some View {
        ZStack(alignment: .topTrailing) {
            ZStack(alignment: .heart) {
                RoundedRectangle(cornerRadius: 12)
                    .foregroundColor(.grey)
                Textual content(title)
                    .font(.title2)
            }
            
            if showEdit {
                Textual content("Edit")
            }
        }
        
    }
}

struct MockStore {
    static func playing cards() -> [Card] {
        [
            Card(title: "Small Card 1", style: .widget1x1, height: 1/1),
            Card(title: "Small Card 2", style: .widget1x1, height: 1/1),
            Card(title: "Small Card 3", style: .widget1x1, height: 1/1),
            Card(title: "Regular2x1", style: .widget2x1, height: 2/1),
            Card(title: "Small Card 4", style: .widget1x1, height: 1/1),
            Card(title: "Large 3x2", style: .widget2x3, height: 2/3),
            Card(title: "Large 2x2", style: .widget2x2, height: 2/2),
            Card(title: "Small Card 5", style: .widget1x1, height: 1/1),
            Card(title: "Small Card 6", style: .widget1x1, height: 1/1)
        ]
    }
}

enum LayoutStyle {
    case widget1x1
    case widget2x1
    case widget2x2
    case widget2x3
}

struct ContentView: View {
    @State personal var playing cards: [Card] = MockStore.playing cards()
    @State personal var isEdit: Bool = false
    @State personal var draggedCard: Card?
    @State personal var offset: CGSize = .zero
    
    var physique: some View {
        GeometryReader { geo in
            ScrollView {
                VStack(alignment: .main) {
                    Button {
                        isEdit.toggle()
                    } label: {
                        Textual content("Edit")
                    }
                    
                    ForEach(groupedCards(), id: .id) { group in
                        LazyHStack {
                            ForEach(group.playing cards) { card in
                                CardView(title: card.title, showEdit: $isEdit)
                                    .aspectRatio(card.top, contentMode: .match)
                                    .body(width: card.type != .widget1x1 ? geo.dimension.width - 32 : (geo.dimension.width - 32) / 2)
                                    .onDrag {
                                        withAnimation(.default) {
                                            self.draggedCard = card
                                            return NSItemProvider()
                                        }
                                    }
                                    .onDrop(of: [.text], delegate:
                                                withAnimation(.default) {
                                        DropViewDelegate(destinationItem: card, playing cards: $playing cards, draggedItem: $draggedCard)})
                                    .background(self.draggedCard == card ? Coloration.clear : Coloration.clear)
                                    .padding(.vertical, isEdit ? 16 : 0)
                            }
                        }.padding(.horizontal)
                    }
                }
                .transaction { transaction in
                    transaction.animation = nil
                }
            }
        }
    }
    
    func groupedCards() -> [CardGroup] {
        var outcome: [CardGroup] = []
        var currentGroup: [Card] = []
        
        for card in playing cards {
            if card.type != .widget1x1 {
                if !currentGroup.isEmpty {
                    outcome.append(CardGroup(playing cards: currentGroup))
                    currentGroup = []
                }
                outcome.append(CardGroup(playing cards: [card]))
            } else {
                if currentGroup.depend == 1 {
                    if currentGroup.first?.type == .widget1x1 {
                        currentGroup.append(card)
                        outcome.append(CardGroup(playing cards: currentGroup))
                        currentGroup = []
                    } else {
                        outcome.append(CardGroup(playing cards: currentGroup))
                        currentGroup = [card]
                    }
                } else {
                    currentGroup.append(card)
                }
            }
        }
        
        if !currentGroup.isEmpty {
            outcome.append(CardGroup(playing cards: currentGroup))
        }
        
        return outcome
    }
}

#Preview {
    ContentView()
}

struct DropViewDelegate: DropDelegate {
    let destinationItem: Card
    @Binding var playing cards: [Card]
    @Binding var draggedItem: Card?
    
    func dropUpdated(data: DropInfo) -> DropProposal? {
        return DropProposal(operation: .transfer)
    }
    
    func performDrop(data: DropInfo) -> Bool {
        draggedItem = nil
        return true
    }
    
    func dropEntered(data: DropInfo) {
        if let draggedItem = draggedItem {
            let fromIndex = playing cards.firstIndex(of: draggedItem)
            if let fromIndex = fromIndex {
                let toIndex = self.playing cards.firstIndex(of: destinationItem)
                if let toIndex = toIndex, fromIndex != toIndex {
                    DispatchQueue.fundamental.asyncAfter(deadline: .now() + 0.2) {
                        withAnimation(.default) {
                            self.playing cards.transfer(fromOffsets: IndexSet(integer: fromIndex), toOffset: (toIndex > fromIndex ? (toIndex + 1) : toIndex))
                        }
                    }
                }
            }
        }
    }
}

May you assist me determine the best way to make the drag-and-drop animations as clean as they had been earlier than grouping the 1×1-sized views right into a single entity? Thanks very a lot!

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles