ios – SwiftUI: Does anybody know why my .onDrag or .onDrop modifiers will not be getting invoked?

0
27
ios – SwiftUI: Does anybody know why my .onDrag or .onDrop modifiers will not be getting invoked?


Beneath are the 2 Views that ought to matter for this query. I am simply attempting to tug and drop a Meal from one place in a Record to a different (similar Record). The concept is to let the consumer modify the time of the precise Meal on their Meal Plan. I really feel like I’ve tried nearly every little thing, however the .onDrop modifier shouldn’t be getting referred to as.

Any assistance on this may be drastically appreciated! I’d like to know what’s stopping this from working.

Here’s a image of what the animation seems to be like after I start dragging:

Dragging Meal

import SwiftUI
import UniformTypeIdentifiers

struct CalendarDayListView2: View {
    
    @State var viewModel: CalendarDayListViewModel2
    @Binding var date: Date
    @Binding var meals: [Meal]
    @Binding var isLoading: Bool
    
    @State personal var scrollToMeal: Meal?
    @Setting(.sizeCategory) var sizeCategory
    @State personal var timeZone: TimeZone = TimeZone.present

    var plusButtonAction: ((Date) -> Void)?
    var xButtonAction: ((Meal) -> Void)?
    var menuButtonAction: ((Meal) -> Void)?
    
    var physique: some View {
        ScrollViewReader { proxy in
            Record {
                ForEach(hoursOfDay, id: .self) { hour in
                    if let meal = mealForHour(hour) {
                        CalendarListItemView(
                            meal: meal,
                            date: hour,
                            mode: viewModel.mode,
                            plusButtonAction: { addMealDate in
                                // Deal with plus button motion
                                self.plusButtonAction?(addMealDate)
                            },
                            xButtonAction: { meal in
                                // Deal with x button motion
                                self.xButtonAction?(meal)
                            }, menuButtonAction: { meal in
                                self.menuButtonAction?(meal)
                            }
                        )
                        .id(meal.id)
                        .swipeActions(edge: .trailing) {
                            Button(function: .damaging) {
                                deleteMeal(meal)
                            } label: {
                                Label("Delete", systemImage: "trash")
                            }
                        }
                        .onDrag {
                            NSItemProvider(object: meal.id as NSString)
                        }
                        .onDrop(of: [UTType.text.identifier], delegate: CalendarDropDelegate(meals: $meals, date: hour))

                    } else {
                        CalendarListItemView(
                            meal: nil,
                            date: hour,
                            mode: viewModel.mode,
                            plusButtonAction: { addMealDate in
                                // Deal with plus button motion
                                self.plusButtonAction?(addMealDate)
                            },
                            xButtonAction: { meal in
                                // Deal with x button motion
                                self.xButtonAction?(meal)
                            }, menuButtonAction: { meal in
                                self.menuButtonAction?(meal)
                            }
                        )
                        .onDrop(of: [UTType.plainText.identifier], delegate: CalendarDropDelegate(meals: $meals, date: hour))

                    }
                }
                .listRowSeparator(.hidden)
                
                // Add additional padding for iPhone SE
                if sizeCategory == .accessibilityExtraExtraExtraLarge {
                    Shade.clear.body(top: 200)
                }
                else {
                    Shade.clear.body(top: 450)
                }
            }
            .body(width: UIScreen.predominant.bounds.width, top: 700)
            .listStyle(PlainListStyle())
            .onChange(of: self.meals) {
                
                scrollToMeal = self.meals.first
            }
            .onChange(of: scrollToMeal) {
                if let meal = scrollToMeal {
                    withAnimation {
                        proxy.scrollTo(meal.id, anchor: .prime)
                    }
                }
            }
            .onChange(of: self.date) { oldValue, newValue in
                
                // Meal Sharing mode
                scrollToMeal = mealsForSelectedDate.first
            }
            .onChange(of: self.isLoading) { oldValue, newValue in
                if !newValue {
                    DispatchQueue.predominant.asyncAfter(deadline: .now() + 0.5) {
                        scrollToMeal = mealsForSelectedDate.first
                    }
                }
            }
        }
        
    }
    
    personal func deleteMeal(_ meal: Meal) {
        if let index = meals.firstIndex(the place: { $0.id == meal.id }) {
            meals.take away(at: index)
        }
    }
    
    personal func dateForHour(_ hour: Int) -> Date {
        let calendar = Calendar.present
        let startOfDay = calendar.startOfDay(for: date)
        return calendar.date(byAdding: .hour, worth: hour, to: startOfDay)!
    }
    
    personal var hoursOfDay: [Date] {
        let calendar = Calendar.present
        var calendarWithTimeZone = calendar
        calendarWithTimeZone.timeZone = timeZone
        let startOfDay = calendarWithTimeZone.startOfDay(for: date)
        return (0..<24).map { hour in
            calendarWithTimeZone.date(byAdding: .hour, worth: hour, to: startOfDay)!
        }
    }
    
    personal var mealsForSelectedDate: [Meal] {
        self.meals.filter { meal in
            guard let plannedAt = meal.plannedAt else { return false }
            return Calendar.present.isDate(plannedAt, inSameDayAs: date)
        }
    }
    
    personal func mealForHour(_ hour: Date) -> Meal? {
        mealsForSelectedDate.first { meal in
            guard var plannedAt = meal.plannedAt else { return false }
            let calendar = Calendar.present
            var calendarWithTimeZone = calendar
            calendarWithTimeZone.timeZone = timeZone
            
            // Convert plannedAt to the present timezone
            plannedAt = plannedAt.convertToTimeZone(timeZone)
            
            return calendarWithTimeZone.isDate(plannedAt, equalTo: hour, toGranularity: .hour)
        }
    }
}

struct CalendarDropDelegate: DropDelegate {
    @Binding var meals: [Meal]
    let date: Date

    func performDrop(information: DropInfo) -> Bool {
        guard let itemProvider = information.itemProviders(for: [.text]).first else { return false }

        itemProvider.loadObject(ofClass: NSString.self) { (id, error) in
            if let id = id as? String,
               let index = meals.firstIndex(the place: { $0.id == id }) {
                DispatchQueue.predominant.async {
                    var updatedMeal = meals[index]
                    print("performDrop: UpdatedMeal date: (date)")
                    updatedMeal.plannedAt = date
                    meals[index] = updatedMeal
                }
            }
        }
        return true
    }
}


// CalendarListItemView.swift
struct CalendarListItemView: View {
    
    var meal: Meal?
    var date: Date
    var mode: CalendarViewMode
    @State var isSelected: Bool = false
    
    var plusButtonAction: ((Date) -> Void)?
    var xButtonAction: ((Meal) -> Void)?
    var menuButtonAction: ((Meal) -> Void)?
    
    var foodImageNotDisplayed = false
    
    var hourString: String {
        let formatter = DateFormatter()
        // Set the locale to make sure AM/PM works accurately in all locales
        formatter.locale = Locale(identifier: "en_US_POSIX")
        // Set the specified format
        formatter.dateFormat = "hh"
        
        // Return the formatted date string
        return formatter.string(from: self.date)
    }
    
    var meridianString: String {
        let formatter = DateFormatter()
        // Set the locale to make sure AM/PM works accurately in all locales
        formatter.locale = Locale(identifier: "en_US_POSIX")
        // Set the specified format
        formatter.dateFormat = "a"
        
        // Return the formatted date string
        return formatter.string(from: self.date)
    }
            
    var physique: some View {
        HStack {
            ZStack {
                RoundedRectangle(cornerRadius: 12.0)
                    .foregroundStyle(.black.opacity(0.03))
                    .body(width: 75, top: self.meal == nil ? 33.0 : 75.0 )
                HStack {
                    Textual content(hourString)
                        .font(Font.customized("Open Sans", dimension: 16))
                        .foregroundColor(.black)
                    Textual content(meridianString)
                        .font(Font.customized("Open Sans", dimension: 16))
                        .foregroundColor(.black.opacity(0.5))
                }
            }
            .padding(.main, 10)
            
            if meal != nil {
                
                HStack {
                    
                    foodImage()
                    
                    VStack {
                        if let meal = meal {
                            
                            Textual content(meal.title)
                                .font(Font.customized("Open Sans", dimension: 15))
                                .foregroundColor(.black)
                        }
                        HStack {
                            if let energy = meal?.energy {
                                Textual content("(energy.formatDouble()) cals")
                                    .font(Font.customized("Open Sans", dimension: 13))
                                    .foregroundColor(Shade(crimson: 0.54, inexperienced: 0.72, blue: 0.13))
                            }

                            if let readyInMinutes = meal?.readyInMinutes {
                                Textual content("• (readyInMinutes) minutes")
                                    .font(Font.customized("Open Sans", dimension: 13))
                                    .foregroundColor(Shade(crimson: 0.54, inexperienced: 0.72, blue: 0.13))
                            }

                        }
                    }
                    
                }
            }
            
            Spacer()
            
            if let meal = meal {
                Button(motion: {
                    if self.mode == .mealSharing {
                        self.menuButtonAction?(meal)
                    }
                    else {
                        xButtonAction?(meal)
                    }
                }, label: {
                    if self.mode == .mealSharing {
                        Picture("menu_button")
                    }
                    else {
                        Picture("x_button")
                    }
                })
                .padding(.trailing, 10)
            }
            // No meal for time slot
            else {
                
                Button(motion: {
                    self.isSelected.toggle()
                    if self.mode == .timeSlot {
                        self.plusButtonAction?(date)
                    }
                    
                }, label: {
                    if self.mode == .timeSlot && self.isSelected {
                        Picture("green_checkmark")
                    }
                    else if self.mode == .mealSharing {
                        EmptyView()
                    }
                    else {
                        Picture("plus_button")
                    }
                })
                .padding(.trailing, 10)
            }
        }
        .body(top: self.meal == nil ? 50.0 : 75.0)
    }
        
    @ViewBuilder
    func foodImage() -> some View {
        if let imageUrl = meal?.picture, let url = URL(string: imageUrl) {
            AsyncImage(url: url) { section in
                if let picture = section.picture {
                    picture
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .body(width: 75, top: 75)
                        .cornerRadius(10)
                } else if section.error != nil {
                    Picture("placeholder_food_image")
                        .resizable()
                        .scaledToFill()
                        .body(width: 75, top: 75)
                        .cornerRadius(10)
                        .clipped()
                } else {
                    Picture("placeholder_food_image")
                        .resizable()
                        .scaledToFill()
                        .body(width: 75, top: 75)
                        .cornerRadius(10)
                        .clipped()
                }
            }
            .body(width: 75, top: 75)
        } else {
            Picture("placeholder_food_image")
                .resizable()
                .scaledToFill()
                .body(width: 75, top: 75)
                .cornerRadius(10)
                .clipped()
        }
    }
}

I attempted the .onMove modifier and that didn’t work as properly. I attempted a number of completely different UTTypes for the .onDrop and that didn’t work. The loopy factor is I even examined making the ForEach merely iterate and show Textual content views of the hour and it nonetheless wouldn’t let me drop them.

LEAVE A REPLY

Please enter your comment!
Please enter your name here