Удаление последнего элемента в списке из подробного представления в SwiftUI не приводит к появлению подробного представления

Я работаю над приложением и столкнулся со странным поведением List и NavigationLink при удалении последнего элемента в списке из подробного представления. Я использую iOS 13 и Xcode 11, и я сделал упрощенную версию, которая воспроизводит поведение:

import SwiftUI

struct ListView: View {
    @State private var content = [Int](0..<10) {
        didSet {
            print(content)
        }
    }

    var body: some View {
        NavigationView {
            List(content, id: \.self) { element in
                NavigationLink(
                    destination: DetailView(
                        remove: {
                            DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1) { // Asynchronous network request
                                DispatchQueue.main.async {
                                    self.content.removeAll { $0 == element } // When request was successfull remove element from list
                                }
                            }
                        }
                    )
                ) {
                    Text("Element #\(element)")
                }
            }
        }
    }
}

struct DetailView: View {

    let remove: () -> Void

    init(remove: @escaping () -> Void) {
        self.remove = remove
    }

    var body: some View {
        VStack {
            Text("Hello world!")

            Button(
                action: {
                    self.remove()
                }
            ) {
                Image(systemName: "trash.circle")
                    .imageScale(.large)
            }
        }
    }
}

Чтобы восстановить ошибку, выберите последний элемент в списке и нажмите корзину, чтобы удалить. Как вы могли заметить, вид не исчезает, как другие элементы из списка. Но если вы нажмете назад, в списке будет правильно удален последний элемент. Это также показано в этом gif. Изменение состояния списка выводится на консоль при нажатии корзины.

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

Я также нашел несколько обходных путей. Подобно изменению NavigationView с помощью .id (UUID ()), но при этом удаляются анимации. Другое решение - отклонить представление с помощью PresentationMode и при исчезновении удалить элемент из списка, но я бы предпочел использовать другое решение.

Чтобы узнать, связано ли это с iOS 13 или Xcode 11, я протестировал его на новейшей бета-версии iOS 14 и Xcode 12 (в настоящее время бета 5). Здесь подробное представление не закрывается ни с одним из выбранных элементов.

Кто-нибудь сталкивался с этой проблемой раньше или хотя бы может объяснить, почему это так?

РЕДАКТИРОВАТЬ: добавлен фиктивный сетевой запрос, чтобы лучше проиллюстрировать конкретную проблему.


person Konstantin    schedule 25.08.2020    source источник


Ответы (1)


SwiftUI там ведет себя немного странно, я думаю ...

Вы можете явно указать SwiftUI отклонить DetailView. Для этого вам нужно немного изменить DetailView:

struct DetailView: View {
    @Environment(\.presentationMode) var presentationMode
    
    let remove: () -> Void
    
    init(remove: @escaping () -> Void) {
        self.remove = remove
    }
    
    var body: some View {
        VStack {
            Text("Hello world!")
            
            Button(
                action: {
                    self.remove()
                    self.presentationMode.wrappedValue.dismiss()
                }
            ) {
                Image(systemName: "trash.circle")
                    .imageScale(.large)
            }
        }
    }
}

(См. Также: iOS SwiftUI: программно отображать или закрывать представление)

На мой взгляд, это работает даже с последней строкой.

person cbjeukendrup    schedule 25.08.2020
comment
Спасибо. Это очень похоже на решение, которое я уже упоминал в сообщении, и оно отлично работает. Но я забыл указать, что удаление фактически запускается сетевым запросом, который происходит, когда вы нажимаете кнопку. Я отредактировал вопрос, чтобы лучше отразить это. Может быть, я смогу сделать что-то подобное, но в модификаторе .onRecieve. - person Konstantin; 25.08.2020