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

TLDR: В следующем коде нажатие кнопки на верхнем уровне ContentView открывает несколько листов, а затем при закрытии листа он запускается снова; однако щелчок по нему в RowView открывает только один лист. Как я могу открыть только один лист из ContentView?

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

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


import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack {
            List {
                NavigationLink {
                    Text("Hello")
                } label: {
                    RowView(textToShow: "Text 1")
                }
                NavigationLink {
                    Text("Hello")
                } label: {
                    RowView(textToShow: "Text 2")
                }
            }
        }
    }
}

struct RowView: View {
    var textToShow: String
    @State private var showingPopover = false
    
    var body: some View {
        Text("Hello")
        
        Button(action: {
            //showingPopover = true
            print("Button pressed")
        }, label: {
            Image(systemName:"doc.text")
                .imageScale(.large)
                
        })
        .onTapGesture {
            print("Button tapped")
            showingPopover = true
        }
        .sheet(isPresented: $showingPopover, content: {
            PopupView(textToShow: textToShow)
                .onAppear {
                    print("Sheet appeared")
                }
        })
    }
}

struct PopupView: View {
    var textToShow: String
    @State private var loading = true
    var body: some View {
        VStack {
            if loading {
                HStack{
                    ProgressView()
                    Text("Loading...")
                }
            } else {
                Text(textToShow)
            }
        }
        .onAppear() {
            print("Loading")
            loading = false
        }
    }
}

#Preview {
    ContentView()
}

#Preview {
    RowView(textToShow: "Text 1")
}

Нажатие кнопки, а затем закрытие листа приводит к следующему результату в консоли:

Нажата кнопка
Загрузка
Лист появился
Загрузка
Лист появился
Загрузка
Лист появился

Я попытался использовать переменную состояния, чтобы фиксировать, когда всплывающее окно уже открыто, а затем отображать содержимое листа только в том случае, если всплывающее окно не было открыто; Я попытался установить переменную привязки на true только для первого жеста касания, но это тоже не решение.

Основываясь на другом аналогичном вопросе, я думаю, что мне нужно переместить лист вверх, что немного сложнее, чем захватить текст для конкретной строки (я использую Swift уже около 2 недель? Так что не совсем к этому привык), но Я думаю, это может сработать

Я ожидаю, что только один лист загрузит и выполнит задачу.

Какую версию Xcode и iOS вы используете? Попробовал ваш код в Xcode 14.3.1, но не вижу проблемы. Я могу попробовать Xcode 15, если это применимо к вам. Кстати, вам следует обернуть RowView в представление, например HStack или VStack, но похоже, что это вообще не связано с вашей проблемой.

shim 12.04.2024 20:25

Спасибо! Я использую Xcode 15.3 и iPhone 15. Кстати, я сделал несколько небольших обновлений кода, чтобы лучше отразить мой фактический вариант использования (каждая строка будет иметь переменную для текста, отображаемого во всплывающем окне).

learnandgrow 12.04.2024 20:28

ОК, я попробую на другой машине — Обновлено: да, я получаю то же самое на Xcode 15.2, хотя журналы появляются в 2 раза вместо 3х.

shim 12.04.2024 20:29

Хорошо, спасибо за подтверждение того, что это новинка для этой версии Xcode. Для меня 3x было, когда я закрывал лист (кажется, странное время для вызова листа onAppear)

learnandgrow 12.04.2024 20:38

о, тогда да, с моей стороны то же самое. Похоже, это ошибка, хотя в целом onAppear сложно использовать для определенных типов логики.

shim 12.04.2024 20:41

Не уверен, что это применимо, но вместо этого вы можете использовать .task. Обновлено: о, но, похоже, это также вызывается несколько раз :S Обратите внимание, что эта проблема, похоже, специфична для представлений внутри NavigationLink — удалите это, и этого не произойдет (хотя, очевидно, если вам нужна навигация, это не так просто).

shim 12.04.2024 20:43

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

shim 12.04.2024 20:50

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

learnandgrow 12.04.2024 20:59
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
8
86
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Хорошо, я думаю, что нашел решение: переместив лист в NavigationStack, он появится только один раз. Чтобы комментарий исходил из переменной, зависящей от строки, я просто использую переменную привязки. В этом случае я использовал isPresented и отдельную переменную, но в моем случае я думаю, что буду использовать вход item для листа.

import SwiftUI

struct ContentView: View {
    @State var showingPopover: Bool = false
    @State var textToShow: String = ""
    var body: some View {
        NavigationStack {
            List {
                NavigationLink {
                    Text("Hello")
                } label: {
                    RowView(showingPopover: $showingPopover, textToShow: $textToShow, textValue: "Text 1")
                }
                
                NavigationLink {
                    Text("Hello")
                } label: {
                    RowView(showingPopover: $showingPopover, textToShow: $textToShow, textValue: "Text 2")
                }
            }
        }
        .sheet(isPresented: $showingPopover, onDismiss: {
            print("Sheet dismissed")
            showingPopover = false
            textToShow = ""
        }, content: {
                PopupView(textToShow: textToShow)
                    .onAppear {
                        print("Popup appeared")
                    }
        })
    }
    
}

struct RowView: View {
    @Binding var showingPopover: Bool
    @Binding var textToShow: String
    var textValue: String
    
    var body: some View {
        Text("Hello")
        
        Button(action: {
            //showingPopover = true
            print("Button pressed")
        }, label: {
            Image(systemName:"doc.text")
                .imageScale(.large)
                
        })
        .onTapGesture {
            print("Button tapped")
            textToShow = textValue
            showingPopover = true
        }
        
    }
}

struct PopupView: View {
    var textToShow: String
    @State private var loading = true
    
    var body: some View {
        VStack {
            if loading {
                HStack{
                    ProgressView()
                    Text("Loading...")
                }
            } else {
                Text(textToShow)
            }
        }
        .onAppear() {
            print("Loading")
            loading = false
        }
    }
}

#Preview {
    ContentView()
}

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

Похожие вопросы

В быстром расширении статического var, как вы можете получить доступ к конкретному классу и вернуть его?
Как отобразить несколько данных за один месяц с помощью библиотеки диаграмм SwiftUI на гистограмме?
Развертывание JSON вручную без кодирования – Swift
Глубокая ссылка не обеспечивает переход к ожидаемому местоположению, если приложение удалено из памяти в быстрой iOS
Продукт пакета Swift не найден при добавлении через зависимость Package.swift
Состояние SwiftUI Bool не обновлено или неверно
Неправильное положение кривой нижнего правого угла (быстрая)
Выравнивание пользовательского интерфейса выглядит не очень хорошо, отображаются точки, как это исправить?
Класс SwiftData @Model, порядок переключения операторов в блоке инициализации неожиданно нарушит представление
Я столкнулся с ошибкой при выполнении запроса POST API: «Ошибка анализа JSON»