Призрачный вид сохраняется через обработчик tapGesture - PullRequest
0 голосов
/ 09 апреля 2020

У меня есть представление, которое зависит как от @ObservedObject, так и от @State.

. В действии кнопки я изменяю их обоих, и это приводит к созданию sh. Если я изменяю только @ObservedObject, то все в порядке (за исключением того, что я, конечно, не получаю нужного поведения).

public struct PointListEditorView: View {
    @ObservedObject var viewModel:          PointListEditorViewModel
    @State          var selectedCellIndex:  Int = NO_CELL_SELECTED_INDEX

    public var body: some View {
        (...)
        Button(action: {
            let savedSelectedCellIndex  = self.selectedCellIndex
            self.selectedCellIndex  = NO_CELL_SELECTED_INDEX     // No crash if I remove this line
            self.viewModel.removePointEditorViewModel(at: savedSelectedCellIndex)
        },
               label: (...)

Что происходит, если у меня есть cra sh в body подпредставления после вызова removePointEditorViewModel. Это тело проходит через массив объектов, который изменяется removePointEditorViewModel. removePointEditorViewModel запускает переменные @Published в @ ObservedObject.

То же самое происходит, если я инвертирую обе строки следующим образом:

self.viewModel.removePointEditorViewModel(at: savedSelectedCellIndex)
self.selectedCellIndex  = NO_CELL_SELECTED_INDEX//##

Сначала я подумал, что будет какое-то странное помехи между @State и @ObservedObject, но первые ответы (спасибо, ребята) указали мне в другом направлении.

Правка для получения дополнительной информации

Правка 2 , чтобы сделать заголовок и остальные в соответствии с текущими исследованиями

Вот иерархия моих представлений:

PointListEditorView
+ PointListEditorContentView
  + PointListEditorCellView (n times)

В PointListEditorView есть selectedCellIndex @State. Это состояние связывается с помощью PointListEditorContentView и PointListEditorCellView. Он изменяется через PointListEditorCellView через tapGesture.

Я зарегистрировал вход и выход из вычисления тела. Я также зарегистрировал создание PointListEditorCellView, и когда я удалил вещи в моей модели. Я заметил некоторые странные вещи.

**APP START**
Enter PointListEditorView body
Exit PointListEditorView body
Enter PointListEditorContentView body
    count of pointEditorViewModels : 0
    count of pointEditorViewModelsAndIndex : 0
End preparation PointListEditorContentView body
Exit PointListEditorContentView body

**ADD ONE CELL**
Enter PointListEditorContentView body
count of pointEditorViewModels : 1
    map call with index : 0
count of pointEditorViewModelsAndIndex : 1
End preparation PointListEditorContentView body
Exit PointListEditorContentView body
created PointListEditorCellView : 35779A71-811F-42DD-A803-3C0E82C3CAD8
Enter PointListEditorCellView body : 35779A71-811F-42DD-A803-3C0E82C3CAD8
Exit PointListEditorCellView body
Enter PointListEditorView body
Exit PointListEditorView body

Все это выглядит довольно нормально для меня. Но теперь:

**SELECT THE CELL**
Enter PointListEditorView body
Exit PointListEditorView body
Enter PointListEditorContentView body
    count of pointEditorViewModels : 1
        map call with index : 0
    count of pointEditorViewModelsAndIndex : 1
End preparation PointListEditorContentView body
Exit PointListEditorContentView body
Enter PointListEditorCellView body : 35779A71-811F-42DD-A803-3C0E82C3CAD8   
           <== Why the hell do we have that ???
               This is the cell view created at previous step. It
               should be forgotten and replaced by another one
               as seen below
Exit PointListEditorCellView body
created PointListEditorCellView : 2EA80249-67B6-46A0-88C9-C5F5E8FEAE80
Enter PointListEditorCellView body : 2EA80249-67B6-46A0-88C9-C5F5E8FEAE80
Exit PointListEditorCellView body

**DELETE THE CELL**
Enter delete callback
    Delete model element
    @Published var modified, the view is notified
Exit delete callback
Enter PointListEditorView body
Exit PointListEditorView body
Enter PointListEditorContentView body
    count of pointEditorViewModels : 0
    count of pointEditorViewModelsAndIndex : 0
        <== This shows there is nothing in the model, there should be
            no cell view created - just like during app init
End preparation PointListEditorContentView body
Exit PointListEditorContentView body
Enter PointListEditorCellView body : 2EA80249-67B6-46A0-88C9-C5F5E8FEAE80
Fatal error: Index out of range: file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444
        <== This "ghost" cell is refering to a model elemnt that
            has been deleted, os of course there is a crash, but why
            does this cell still exist ???

После еще нескольких расследований мне удалось обнаружить, что «сохранение» происходит только тогда, когда к представлению ячейки прикреплен tapGesture. Это делается в представлении PointListEditorContentView следующим образом:

struct PointListEditorCellView: View
    var pointEditorViewModel:       PointEditorBaseViewModel
    var index:                      Int
    @Binding var selectedCellIndex: Int

    var body: some View {
        VStack(spacing: 3.0) {
            PointEditorView(pointEditorViewModel)
            .onTapGesture {
                if (self.selectedCellIndex == self.index) {
                    self.selectedCellIndex = NO_CELL_SELECTED_INDEX
                } else {
                    self.selectedCellIndex = self.index
                }
            }
    }
}

Если я удалю .gesture, я не вижу появления призрака.

Ответы [ 2 ]

0 голосов
/ 11 апреля 2020

Я наконец нашел решение.

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

У меня был следующий код:

struct PointListEditorContentView: View {
    @ObservedObject var viewModel: PointListEditorBaseViewModel

    init(_ vm:              PointListEditorBaseViewModel) {
        self.viewModel   = vm
    }

    var body: some View {
        let pointEditorViewModels           = viewModel.pointEditorViewModelsList()
        let pointEditorViewModelsAndIndex   = pointEditorViewModels.enumerated().map { (index, vm) in
            return (vm: vm,
                    index: index)
        }

        return VStack(spacing: 3.0) {
            ForEach(pointEditorViewModelsAndIndex, id: \.vm.id) { pointEditorViewModel in
                PointListEditorCellView(pointListEditorViewModel:   self.viewModel,
                                        pointEditorViewModel:       pointEditorViewModel.vm,
                                        index:                      pointEditorViewModel.index)
            }
            PointListEditorDropableView(viewModel:              viewModel,
                                        positionOfDroppedItem:  pointEditorViewModels.count)
        }
        .padding()
    }
}


struct PointListEditorCellView: View {
    @ObservedObject var pointListEditorViewModel:   PointListEditorBaseViewModel
    @ObservedObject var pointEditorViewModel:       PointEditorBaseViewModel
                    var index:                      Int

    var body: some View {
        VStack(spacing: 3.0) {
        PointListEditorDropableView(viewModel:              pointListEditorViewModel,
                                    positionOfDroppedItem:  index)
            .fixedSize(horizontal: false, vertical: true)

        PointEditorView(pointEditorViewModel)
            .shadow(color:  (self.pointListEditorViewModel.selectedCellIndex == index ? Color.accentColor : Color.clear),
                    radius: (self.pointListEditorViewModel.selectedCellIndex == index ? 5.0 : 0.0))
            .onTapGesture {
                if (self.pointListEditorViewModel.selectedCellIndex == self.index) {
                    self.pointListEditorViewModel.selectedCellIndex  = NO_CELL_SELECTED_INDEX
                } else {
                    self.pointListEditorViewModel.selectedCellIndex = self.index
                }
            }
            .onDrag {
                let itemProvider    = NSItemProvider()
                itemProvider.registerObject(PointListEditorItemProvider(initialIndex: self.index),
                                            visibility:  .ownProcess)
                return itemProvider
            }
        }
    }
}

Я только что переместил содержимое тела PointListEditorCellView внутри ForEach, как в следующем коде, и все внезапно пошло хорошо.

struct PointListEditorContentView: View {
    @ObservedObject var viewModel: PointListEditorBaseViewModel

    init(_ vm:              PointListEditorBaseViewModel) {
        self.viewModel   = vm
    }

    var body: some View {
        let pointEditorViewModels           = viewModel.pointEditorViewModelsList()
        let pointEditorViewModelsAndIndex   = pointEditorViewModels.enumerated().map { (index, vm) in
            return (vm: vm,
                    index: index)
        }

        return VStack(spacing: 3.0) {
            ForEach(pointEditorViewModelsAndIndex, id: \.vm.id) { pointEditorViewModel in
                VStack(spacing: 3.0) {
                    PointListEditorDropableView(viewModel:              self.viewModel,
                                                positionOfDroppedItem:  pointEditorViewModel.index)
                        .fixedSize(horizontal: false, vertical: true)

                    PointEditorView(pointEditorViewModel.vm)
                        .shadow(color:  (self.viewModel.selectedCellIndex == pointEditorViewModel.index ?
                                                                                Color.accentColor : Color.clear),
                                radius: (self.viewModel.selectedCellIndex == pointEditorViewModel.index ? 5.0 : 0.0))
                        .onTapGesture {
                            if (self.viewModel.selectedCellIndex == pointEditorViewModel.index) {
                                self.viewModel.selectedCellIndex  = NO_CELL_SELECTED_INDEX
                            } else {
                                self.viewModel.selectedCellIndex = pointEditorViewModel.index
                            }
                        }
                        .onDrag {
                            let itemProvider    = NSItemProvider()
                            itemProvider.registerObject(PointListEditorItemProvider(initialIndex:
                                                                                    pointEditorViewModel.index),
                                                                                    visibility:  .ownProcess)
                            return itemProvider
                    }
                }
            }

            PointListEditorDropableView(viewModel:              viewModel,
                                        positionOfDroppedItem:  pointEditorViewModels.count)
        }
        .padding()
    }
}

Я документирую это в SO, так что если у кого-то есть другое (и лучшее объяснение, чем просто «это ошибка SwiftUI») Мы можем записать обсуждение.

0 голосов
/ 09 апреля 2020

Нет помех между @State и @ObservedObject. С небольшим предоставленным вами кодом, я бы внимательно посмотрел на

self.viewModel.removePointEditorViewModel(at: savedSelectedCellIndex)

, является ли индекс (SavedSelectedCellIndex) действительным для этой модели?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...