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.
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!