Перезагрузка SwiftUI swipeActions приводит к пустому пространству и прерывистому свайпу

Я пытаюсь использовать настройку модификатора SwiftUI swipeДействия, как показано в приведенном ниже коде, но действие смахивания отключается, как показано на этом gif:
Перезагрузка SwiftUI swipeActions приводит к пустому пространству и прерывистому свайпу

struct ContentView: View {
@ObservedObject var viewModel: ViewModel

var body: some View {
    if viewModel.items.count > 0 {
        ZStack {
            List {
                ForEach(viewModel.items, id: \.self) { item in
                    Text(item)
                        .swipeActions {
                            Button {
                                viewModel.removeAction(item: item)
                            } label: {
                                Text("Remove")
                            }
                            .tint(.orange)
                        }
                }
            }
            
        }
    } else {
        ProgressView()
            .foregroundColor(.accentColor)
            .scaleEffect(2)
    }
}

В модели представления после первого свайпа я бы перезагрузил список из API (пример кода просто имитирует задержку):

extension ContentView {
class ViewModel: ObservableObject {
    @Published var items: [String]
    
    init(items: [String]) {
        self.items = items
    }
    
    func removeAction(item: String) {
        if let index = items.firstIndex(where: { $0 == item }) {
            items.remove(at: index)
        }
        
        let itemsSaved = items
        items = []
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
            self.items = itemsSaved
        }
    }
}

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

Фактическое поведение: каждая строка имеет вид пробела в начале строки, вы не можете прокручивать строки, как раньше.

Перезагрузка SwiftUI swipeActions приводит к пустому пространству и прерывистому свайпу

Я также создал пример проекта: код и дополнительный видео.

Любая идея, если есть обходной путь?

Спасибо.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
35
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я до сих пор не уверен, почему ваша реализация вызывает такое поведение, кроме того, что вы полностью переключаетесь между двумя отдельными представлениями (Zstack против ProgressView). Я подозреваю, что изменение туда и обратно просто переводит List в какое-то странное состояние. Однако исправить это просто; поместите условное выражение внутри ZStack:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        ZStack {
             // Move the conitional inside of the ZStack.
             // Always use .isEmpty for this sort of test. Faster and
             // less resources than count
             if !viewModel.items.isEmpty {
                  List {
                      // I made Item an Identifiable struct. Deleting items
                      // identified as .self can lead to issues in a ForEach
                      ForEach(viewModel.items) { item in
                           Text(item.name)
                               .swipeActions {
                                    Button {
                                        viewModel.removeAction(item: item)
                               } label: {
                                    Text("Remove")
                               }
                                    .tint(.orange)
                           }
                      }
                  }
             } else {
                  Text("Progress View")
             }
         }
    }
}

extension ContentView {
    class ViewModel: ObservableObject {
        @Published var items: [Item]
        
        init(items: [Item]) {
            self.items = items
        }
        
        func removeAction(item: Item) {
            if let index = items.firstIndex(where: { $0 == item }) {
                items.remove(at: index)
            }
            
            let itemsSaved = items
            items = []
            
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
                self.items = itemsSaved
            }
        }
    }
}

// I made this as a data model. 
struct Item: Identifiable, Hashable {
    var id = UUID()
    var name: String
}

Хорошая мысль о empty. Кажется, я иногда забываю об этом.

Zsolt 11.05.2022 17:47

Спасибо @Yrb. Я сам поиграл с этим и пришел к такому же выводу, но у меня также есть другие условные выражения (if/else), которые я не могу переместить в ZStack. Например. Возможно, мне придется показать ошибку из API. Интересно, есть ли что-то, что все еще может вызвать сброс List, чтобы восстановить сломанное состояние.

Zsolt 11.05.2022 17:51

Еще одна вещь, которую я обнаружил, исправлена ​​​​просто в том, что данные не очищались. То, что вы здесь сделали, не имеет большого смысла. Вы удаляете один элемент, затем удаляете ВСЕ элементы, а затем перезагружаете их. Если вы не удалили их все, а просто позволили обновлению обновить их, у вас нет проблемы. Я предположил, что это MRE, поэтому я не сильно сомневаюсь в логике, когда пытаюсь ответить, если только она не кажется неотъемлемой частью ответа.

Yrb 11.05.2022 18:39

Да, спасибо @Yrb. Это взад и вперед не имеет смысла в этом примере проекта, но в приложении, над которым я работаю, это связано с функциями поиска/вызова API/удаления/добавления.

Zsolt 12.05.2022 14:01

Другие вопросы по теме