SwiftUI: понимание жизненного цикла .sheet / .fullScreenCover при использовании констант и инициализаторов @Binding

Я пытаюсь понять, как и когда вызываются инициализаторы .sheet и .fullScreenCover. Ниже приведен минимальный воспроизводимый пример, где первый экран имеет 3 цветных прямоугольника, а SecondView (показанный через .fullScreenCover) имеет 1 прямоугольник, который меняет цвет в зависимости от выбранного цвета на первом экране.

  • При первой загрузке приложения устанавливается серый цвет.
  • Если я нажму на зеленый прямоугольник, SecondView представит серый прямоугольник. (т.е. цвет НЕ изменился правильно).
  • Если я затем закрою SecondView и коснусь красного прямоугольника, SecondView представит красный прямоугольник. (т.е. цвет ДЕЙСТВИТЕЛЬНО изменился правильно.)

Итак, мне интересно, почему эта настройка НЕ ​​работает при начальной загрузке, но работает со 2-й или 3-й попытки?

Примечание. Я понимаю, что это можно решить, изменив «let selectedColor» на переменную @Binding, это не то, о чем я прошу.

Код:

import SwiftUI

    struct SegueTest: View {
        
        @State var showSheet: Bool = false
        @State var color: Color = .gray
        
        var body: some View {
            HStack {
                RoundedRectangle(cornerRadius: 25)
                    .fill(Color.red)
                    .frame(width: 100, height: 100)
                    .onTapGesture {
                        color = .red
                        showSheet.toggle()
                    }
                
                RoundedRectangle(cornerRadius: 25)
                    .fill(Color.green)
                    .frame(width: 100, height: 100)
                    .onTapGesture {
                        color = .green
                        showSheet.toggle()
                    }
    
                RoundedRectangle(cornerRadius: 25)
                    .fill(Color.orange)
                    .frame(width: 100, height: 100)
                    .onTapGesture {
                        color = .orange
                        showSheet.toggle()
                    }
    
            }
            .fullScreenCover(isPresented: $showSheet, content: {
                SecondView(selectedColor: color)
            })
        }
    }
    
    struct SecondView: View {
        
        @Environment(\.presentationMode) var presentationMode
        let selectedColor: Color // Should change to @Binding
        
        var body: some View {
            ZStack {
                Color.black.edgesIgnoringSafeArea(.all)
                
                RoundedRectangle(cornerRadius: 25)
                    .fill(selectedColor)
                    .frame(width: 300, height: 300)
            }
            .onTapGesture {
                presentationMode.wrappedValue.dismiss()
            }
        }
    
    }
    
    struct SegueTest_Previews: PreviewProvider {
        static var previews: some View {
            SegueTest()
        }
    }
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
955
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Смотрите комментарии и print заявления. Особенно red

import SwiftUI

struct SegueTest: View {
    
    @State var showSheet: Bool = false{
        didSet{
            print("showSheet :: didSet")
        }
        willSet{
            print("showSheet :: willSet")
        }
    }
    @State var color: Color = .gray{
        didSet{
            print("color :: didSet :: \(color.description)")
        }
        willSet{
            print("color :: willSet :: \(color.description)")
        }
    }
    @State var refresh: Bool = false
    init(){
        print("SegueTest " + #function)
    }
    var body: some View {
        print(#function)
        return HStack {
            //Just to see what happens when you recreate the View
            //Text(refresh.description)
            Text(color.description)
            RoundedRectangle(cornerRadius: 25)
                .fill(Color.red)
                .frame(width: 100, height: 100)
                .onTapGesture {
                    print("SegueTest :: onTapGesture :: red")
                    //Changing the color
                    color = .red
                    //Refreshed SegueTest reloads function
                    //refresh.toggle()
                    showSheet.toggle()
                }
            
            RoundedRectangle(cornerRadius: 25)
                .fill(Color.green)
                .frame(width: 100, height: 100)
                .onTapGesture {
                    print("SegueTest :: onTapGesture :: green")
                    //Changing the color
                    color = .green
                    showSheet.toggle()
                }
            
            RoundedRectangle(cornerRadius: 25)
                .fill(Color.orange)
                .frame(width: 100, height: 100)
                .onTapGesture {
                    print("SegueTest :: onTapGesture :: orange")
                    //Changing the color
                    color = .orange
                    showSheet.toggle()
                }
            
        }
        //This part is likely created when SegueTest is created and since a struct is immutable it keeps the original value
        .fullScreenCover(isPresented: $showSheet, content: {
            SecondView(selectedColor: color)
        })
    }
}

struct SecondView: View {
    
    @Environment(\.presentationMode) var presentationMode
    //struct is immutable
    let selectedColor: Color // Should change to @Binding
    init(selectedColor: Color){
        print("SecondView " + #function)
        self.selectedColor = selectedColor
        print("SecondView :: struct :: selectedColor = \(self.selectedColor.description)" )
        print("SecondView :: parameter :: selectedColor = \(selectedColor.description)" )
    }
    var body: some View {
        ZStack {
            Color.black.edgesIgnoringSafeArea(.all)
            
            RoundedRectangle(cornerRadius: 25)
                .fill(selectedColor)
                .frame(width: 300, height: 300)
        }
        .onTapGesture {
            presentationMode.wrappedValue.dismiss()
        }
    }
    
}

struct SegueTest_Previews: PreviewProvider {
    static var previews: some View {
        SegueTest()
    }
}

Это действительно полезно - спасибо. Почему refresh.toggle() перезагружает SegueTest, но не меняет цвет? Оба являются переменными @State. Есть ли способ печати при перезагрузке SegueTest?

nicksarno 14.12.2020 01:18

Это было бы, если бы это было упомянуто в SegueTest, измените Text(refresh.description) на Text(color.description) и закомментируйте refresh.toggle() и @State var refresh: Bool = false. Я просто использовал обновление для объяснения/теста или, может быть, привычки. Поставьте print в body. Посмотреть код

lorem ipsum 14.12.2020 01:24

Ооо теперь вижу. Поэтому, если я также закомментирую Text(), представление не будет перерисовываться, и цвет не изменится. Но добавив этот Text(), представление вынуждено повторно отображать перед переходом... верно?

nicksarno 14.12.2020 01:31

Да, именно так это работает. @Binding вызывает рендеринг в SecondView, чтобы он работал каждый раз и не зависел от какого-либо другого обновления.

lorem ipsum 14.12.2020 01:32

Именно то, что я пытался выяснить. ... так что, если я закомментирую Text(), он не будет повторно отображаться... но со 2-й/3-й попытки он будет повторно отображаться. Любая идея, что вызывает это?

nicksarno 14.12.2020 01:38

Вероятно, поведение предварительной загрузки SwiftUI. SwiftUI дает много прогнозов. Это определенно было бы чем-то, чтобы попросить Apple получить внутреннюю работу (во время WWDC, чтобы вы не потеряли возможность задать конкретный вопрос по коду или, возможно, на форумах Apple). Он также не меняется, если вы снова и снова нажимаете один и тот же цвет. Он учится только после того, как вы нажмете второй цвет.

lorem ipsum 14.12.2020 01:42

Да, я видел это, ха-ха. Еще раз спасибо!

nicksarno 14.12.2020 01:44

Проблема в том, что вы не используете свой цвет @State внутри SegueView, который не перезагрузит ваше представление при изменении состояния. Просто включите свой @State где-нибудь в код, что заставит его повторно отображать, а затем обновить лист с правильным цветом.

RoundedRectangle(cornerRadius: 25)
    .fill(color == .red ? Color.red : Color.red) //<< just use dump color variable here

Если вы не укажете свое состояние цвета где-то в коде, он не будет повторно отображать ваш SegueView, поэтому вы все равно передадите старый серый цвет вашему SecondView.

Или даже передать свои цвета как Binding your Second View..

SecondView(selectedColor: $color)

@Binding var selectedColor: Color 
 

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