Опубликованные данные обновляют дочернее представление, но не родительское представление - PullRequest
0 голосов
/ 13 июля 2020

Я создал этот пример, демонстрирующий проблему, с которой я столкнулся с моим приложением. У меня есть представление со свойством, которое родительское представление и само должны использовать для обновления пользовательского интерфейса. Что происходит, так это то, что дочернее представление обновляется, когда свойство изменяется, а родительское представление - нет.

Я пытаюсь достичь с помощью приведенного ниже кода, так как при нажатии на текст появляется синий контур, если состояние истинно, в противном случае нет контура. Что на самом деле происходит, так это то, что дочернее представление обновляется (изменяется текст), но родительское представление остается прежним (синий контур остается прежним).

/********************Child View***************************/
class ExampleChildViewModel: ObservableObject {
    var id = UUID()
    @Published var showOptions: Bool
    
    init( _ show: Bool ) {
        showOptions = show
    }
}

struct ExampleChildView: View {
    @ObservedObject var model: ExampleChildViewModel
    
    var body: some View{
        Text("\(String(model.showOptions))")
        .onTapGesture {
            self.model.showOptions.toggle()
        }
    }
}

/********************Parent View***************************/
class ExampleParentViewModel: ObservableObject {
    @Published var children = [ExampleChildViewModel]()
    
    init(){
        children.append( ExampleChildViewModel( true ) )
        children.append( ExampleChildViewModel( false ) )
    }
}

struct ExampleParentView: View {
    @ObservedObject var parent: ExampleParentViewModel
    
    var body: some View{
        VStack{
            ForEach( parent.children, id: \.id ){ m in
                ExampleChildView( model: m )
                    .frame(width: 60, height: 30)
                    .overlay(
                        RoundedRectangle(cornerRadius: 6)
                            .stroke(Color.blue, lineWidth: 3)
                            .opacity((m.showOptions) ? 1 : 0) // <---- Doesn't update
                )
            }
        }
    }
}

/********************Main View*******************************/
struct ExampleView: View {
    @ObservedObject var model = ExampleParentViewModel()

    var body: some View {
        VStack{
            ExampleParentView( parent: model )
        }
    }
}

Почему parent в ExampleParentView не обновляется, когда showOptions в ExampleChildViewModel установлено?

Изменить:

Это то, что приложение пытается показать / скрыть. Каждая из строк с символами блокировки является дочерними представлениями.

Showing the overlay that exists in the parent

If I have the overlay in the child view I end up with the following

Дочерний оверлей оказывается позади других представлений

Ответы [ 2 ]

1 голос
/ 13 июля 2020

Это немного странно, почему вы определяете внешний вид представления в другом представлении. Обычно вы делаете всю «настройку» в самом представлении (в данном случае - в ChildView) и просто реализуете его в ParentView.

Теперь к проблеме. @Published работает как @State. При изменении он уведомляет представление об обновлении. И в этом твоя проблема. Он уведомляет об обновлении ChildView, но не ParentView. Вы можете увидеть это при изменении ParentView следующим образом:

var body: some View{
    VStack{
        ForEach( self.parent.children, id: \.id ){ m in
            
            ExampleChildView( model: m )
                .opacity( m.showOptions ? 1 : 0)
                .frame(width: 60, height: 30)
                .overlay(
                    RoundedRectangle(cornerRadius: 6)
                        .stroke(Color.blue, lineWidth: 3)
                        .opacity( m.showOptions ? 1 : 0)// <---- Doesn't update
                )
        
        }
        

        
        Text("Current State?").onTapGesture {
            for m in self.parent.children {
                print(m.showOptions)
            }
            print("-----------")
        }
    }
}

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

Текст false / true обновляется по мере того, как он определяется в ChildView. Внедрите Overlay также в ChildView, и он работает так, как вы ожидаете:

struct ExampleChildView: View {
    
    @ObservedObject var model: ExampleChildViewModel
    
    var body: some View{
        Text("\(String(model.showOptions))")
            .onTapGesture {
                self.model.showOptions.toggle()
            }.overlay(
                RoundedRectangle(cornerRadius: 6)
                    .stroke(Color.blue, lineWidth: 3)
                    .opacity( self.model.showOptions ? 1 : 0)
            )
    }
}

Я бы предложил вам реализовать всю или, по крайней мере, основную (изменяющуюся) часть стилизации View в самом представлении.

Также: в этой статье это довольно хорошо объясняется https://www.hackingwithswift.com/books/ios-swiftui/sharing-swiftui-state-with-observedobject

1 голос
/ 13 июля 2020

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

struct ExampleChildView: View {

    @ObservedObject var model: ExampleChildViewModel
    
    var body: some View{
        HStack {
            Text("\(String(model.showOptions))")
        }
        .frame(width: 60, height: 30)
        .overlay(
            RoundedRectangle(cornerRadius: 6)
                .stroke(Color.blue, lineWidth: 3)
                .opacity((model.showOptions) ? 1 : 0))
        .onTapGesture {
            self.model.showOptions.toggle()
        }
    }
}
...