Nano Hash - криптовалюты, майнинг, программирование

Проблема при изменении порядка элемента списка в подробном представлении с использованием SwiftUI Navigation View и Sorted FetchRequest

У меня есть NavigationView со списком задач из CoreData FetchRequest. FetchRequest сортируется по возрастанию Task.dueDate. Представление TaskDetail в основном состоит из TextField для заголовка и средства выбора даты для даты. Изменение значений в подробном представлении работает. Хотя я получаю странное поведение каждый раз, когда пытаюсь изменить значение даты. Дата изменяется, но представление навигации автоматически выходит из представления подробностей и возвращается к представлению списка. Это происходит только тогда, когда я меняю дату таким образом, что список перестраивается из-за сортировки.

Как предотвратить описанное выше странное поведение?

введите описание изображения здесь

//
//  ContentView.swift

import SwiftUI
import CoreData

struct ContentView: View {

    @Environment(\.managedObjectContext) var moc
    @FetchRequest(fetchRequest: Task.requestAllTasks()) var tasks: FetchedResults<Task>

    var body: some View {
        NavigationView {
            List(tasks, id: \.id) { task in
                NavigationLink(destination: TaskDetail(task: task)) {
                    Text("\(task.title)")
                }
            }.navigationBarTitle("Tasks").navigationBarItems(trailing: Button("new") {self.addTask()})
        }
    }

    func addTask() -> Void {
        let newTask = Task(context: self.moc)
        newTask.id = UUID()
        newTask.title = "task \(tasks.count)"
        newTask.dueDate = Date()
        print("created new Task")
        if (self.moc.hasChanges) {
            try? self.moc.save()
            print("saved MOC")
        }
        print(self.tasks)
    }

}

struct TaskDetail : View {

    @ObservedObject var task: Task

    var body: some View {
        VStack{
            TextField("name", text: $task.title)
            DatePicker("dueDate", selection: $task.dueDate, displayedComponents: .date)
                .labelsHidden()
        }
    }
}

//
//  Task.swift

import Foundation
import CoreData

public class Task: NSManagedObject, Identifiable {
    @NSManaged public var id: UUID?
    @NSManaged public var dueDate: Date
    @NSManaged public var title: String

    static func requestAllTasks() -> NSFetchRequest<Task> {
        let request: NSFetchRequest<Task> = Task.fetchRequest() as! NSFetchRequest<Task>

        let sortDescriptor = NSSortDescriptor(key: "dueDate", ascending: true)
        request.sortDescriptors = [sortDescriptor]

        return request
    }
}

Чтобы создать работающую минимально воспроизводимую версию этого ... выполните:

  1. Создайте новый проект Xcode "Single View App". Обязательно установите флажок CoreData.
  2. Скопируйте приведенный выше код ContentView и вставьте / замените его в ContentView.swift.
  3. Создайте новый файл Swift с именем Task. Скопируйте код для задачи и вставьте в Task.swift.
  4. Добавьте объекты в ProjectName.xcdatamodeld в соответствии с изображением ниже.
  5. Запустить

введите описание изображения здесь

Я использую Xcode 11.4.

Дайте мне знать, если вам понадобится дополнительная информация. Любая помощь высоко ценится! Спасибо!


  • Насколько я понял, ваша операция привела к удалению навигационной ссылки из стека. Низкий шанс, но попробуйте установить NavigationLink().id(task). Если не работает, измените дизайн, например. редактирование временного объекта данных, который применяется к базе данных при окончании редактирования. 03.04.2020
  • как насчет того, чтобы показать нам копируемый компилируемый код, который мы можем воспроизвести вместо того, чтобы просто догадываться ....? 03.04.2020
  • @Chris Спасибо за ответ. Я добавил еще код. 03.04.2020
  • @Asperi добавление .id (задача) или .id (task.id) не имело никакого значения. К сожалению, сохранение в базу данных при выходе из подробного представления или при нажатии кнопки сохранения не поддерживается. Кроме того, это стало бы довольно странно на iPad, поскольку приложение для iPad будет отображать список и подробное представление одновременно (стандартное поведение). 03.04.2020
  • Я не имел в виду делать это на уровне пользовательского интерфейса ... в любом случае для тестирования нужен воспроизводимый пример. 03.04.2020
  • @ Аспери, ладно. Я обновил код, а также предоставил несколько инструкций о том, как настроить воспроизводимый пример. 03.04.2020
  • я мог воспроизвести поведение. Я думаю, это потому, что он обновляет значение непосредственно в списке, и поскольку оно отсортировано, оно выскакивает, потому что теперь у него другая задача (а не та, что была в исходной детали навигации). Я думаю, что решение может быть, если вы скопируете задачу, над которой работаете подробно, и измените эту рабочую копию в базе данных после фиксации изменения в подробном просмотре 03.04.2020
  • @ Крис милый. Да, я предполагаю, что это как-то теряет задачу на какое-то время. То, что вы предлагаете, вероятно, сработает, но тогда я не получу требуемого UX. Как я сказал Asperi, мне нужно, чтобы приложение обновляло значения, когда пользователь меняет их, и пока у пользователя все еще открыто подробное представление. 03.04.2020
  • ах ладно, тогда мое решение не то, что вы хотите :( 03.04.2020
  • Я считаю, что это ошибка в iOS 13.4. Если вы запустите код на iOS 13.3 (не забудьте изменить цель в разделе «Информация о развертывании» на вкладке «Общие настройки»), приложение будет работать должным образом. У iOS 13.4 есть другие серьезные проблемы с Core Data, например потеря соединения с постоянным координатором хранилища: stackoverflow.com/questions/60843114 (в моем отредактированном ответе на этот вопрос вы увидите анимированный gif, показывающий похожее ошибочное поведение, с которым вы столкнулись здесь). Предлагаю вам оставить отзыв в Apple. 04.04.2020
  • Ошибка сохраняется в iOS 14 beta 1. 30.06.2020

Ответы:


1

ОБНОВЛЕНИЕ 2 (iOS 14 beta 3)

Проблема, похоже, исправлена ​​в iOS 14 beta 3: подробное представление больше не появляется при внесении изменений, влияющих на порядок сортировки.


ОБНОВЛЕНИЕ

Похоже, Apple видит в этом особенность, а не ошибку; сегодня они ответили на мой отзыв (FB7651251) об этой проблеме следующим образом:

Мы бы рекомендовали использовать isActive и самостоятельно управлять push с помощью привязки выбора, если вы желаете именно такого поведения. Как это ведет себя правильно.

Это связано с тем, что идентификатор перемещаемого представления изменяется при изменении порядка сортировки.


Как упоминалось в моем комментарии выше, я считаю, что это ошибка в iOS 13.4.

Обходной путь может заключаться в использовании NavigationLink вне списка и определении строк списка как кнопок, которые

а) установить задачу для редактирования (новый @State var selectedTask) и

б) вызвать NavigationLink на TaskDetail (задача: selectedTask!).

Эта настройка отсоединит выбранную задачу от ее позиции в отсортированном списке, что позволит избежать неправильного поведения, вызванного повторной сортировкой, которая может быть вызвана редактированием dueDate.

Для достижения этой цели:

  1. добавьте эти две переменные @State в структуру ContentView
    @State private var selectedTask: Task?
    @State private var linkIsActive = false
  1. обновите тело структуры ContentView следующим образом
    var body: some View {
        NavigationView {
            ZStack {
                NavigationLink(
                    destination: linkDestination(selectedTask: selectedTask),
                    isActive: self.$linkIsActive) {
                    EmptyView()
                }
                List(tasks) { task in
                    Button(action: {
                        self.selectedTask = task
                        self.linkIsActive = true
                    }) {
                        NavigationLink(destination: EmptyView()){
                            Text("\(task.title)")
                        }
                    }
                }
            }
            .navigationBarTitle("Tasks").navigationBarItems(trailing: Button("new") {self.addTask()})
        }
    }
  1. добавьте следующую структуру в ContentView.swift
    struct linkDestination: View {
        let selectedTask: Task?
        var body: some View {
            return Group {
                if selectedTask != nil {
                    TaskDetail(task: selectedTask!)
                } else {
                    EmptyView()
                }
            }
        }
    }
06.04.2020

2

Я столкнулся с той же проблемой и не смог найти хорошего решения. Но у меня есть другое решение.

Я сделал Fetchrequest динамическим и изменил дескриптор сортировки, когда ссылка активна. Это имеет отрицательный побочный эффект, заключающийся в том, что список сортируется с анимацией каждый раз, когда вы возвращаетесь к ContentView.

Если вы добавите в свой список следующую структуру:

struct TaskList: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest var tasks: FetchedResults<Task>
    @Binding var activeTaskLink: Int?

    init(activeTaskLink: Binding<Int?>, currentSortKey: String) {
        self._activeTaskLink = activeTaskLink
        self._tasks = Task.requestAllTask(withSortKey: currentSortKey)
    }

    var body: some View {
        List(tasks, id: \.id) { task in
            NavigationLink(destination: TaskDetail(task: task), tag: task.objectId.hashValue, selection: self.$activeTaskLink) {
                Text("\(task.title)")
            }
        }
    }

}

Затем измените функцию requestAllTask ​​в Task.swift:

static func requestAllTasks(withSortKey key: String) -> NSFetchRequest<Task> {
    let request: NSFetchRequest<Task> = Task.fetchRequest() as! NSFetchRequest<Task>

    let sortDescriptor = NSSortDescriptor(key: key, ascending: true)
    request.sortDescriptors = [sortDescriptor]

    return request
}

Затем добавьте состояние для activeTask в ContentView

@State var activeTaskLink: Int? = nil

и измените тело на

var body: some View {
    TaskList(activeTaskLink: self.$activeTaskLink, currentSortKey: self.activeNavLink != nil ? "id" : "dueDate")
    .navigationBarTitle("Tasks")
    .navigationBarItems(trailing: Button("new") {self.addTask()})
}
15.04.2020
  • Если я правильно понимаю, список не будет отсортирован, пока вы не покинете подробное представление? Но что происходит, когда вы запускаете приложение на iPad и одновременно отображаются главное и подробное представление? Не будет ли это странно? 22.04.2020
  • Да, ты прав. Это не будет работать правильно с представлением "главный-подробный". Я не думаю, что. 27.04.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..