SwiftUI устанавливает переменные состояния через другой экземпляр представления - PullRequest
0 голосов
/ 27 мая 2020

В SwiftUI я создал структуру, которая должна создавать различные представления наложения в зависимости от некоторых переменных состояния. Если какое-либо из логических значений состояния истинно, оно должно возвращать настраиваемое представление (либо ErrorOverlay, либо LoadingOverlay, либо EmptyView) следующим образом:

struct OverlayContainer: View {
    @State var isLoading: Bool = false
    @State var isErrorShown: Bool = false

    func setIsLoading(isLoading: Bool) {
        self.isLoading = isLoading
    }

    func setIsErrorShown(isErrorShown: Bool) {
        self.isErrorShown = isErrorShown
    }

    var body: some View {
        Group {
            if(isErrorShown) {
                ErrorOverlay()
            }
            else if(isLoading) {
                LoadingOverlay()
            }
            else {
                EmptyView()
            }
        }
    }
}

Теперь я реализовал наложение для некоторого содержимого в домашнем представлении с кнопками, которые должны изменить состояние и показать правильное наложение, например:

struct Home: View {

    var body: some View {
        let overlayContainer = OverlayContainer()

        return HStack {
            // Some more content here

            Button(action: {
                overlayContainer.setIsLoading(isLoading: true)
            }) {
               Text("Start loading")
            }
            Button(action: {
                overlayContainer.setIsErrorShown(isErrorShown: true)
            }) {
               Text("Show error")
            }
        }.overlay(overlayContainer)
    }
}

Это не работает: когда я нажимаю кнопку, ничего не происходит. Почему и как это решить? (без привязки см. ниже)


пс. Мне удалось получить рабочее решение, выполнив следующие действия:

  1. извлечение логических значений состояния в домашнее представление
  2. передать их через конструктор OverlayContainer
  3. изменить логические значения состояния вместо вызова методов набора при нажатии кнопок
  4. измените OverlayContainer, чтобы он реализовал метод init с обоими логическими значениями
  5. измените логические значения состояния в OverlayContainer на bindings.

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

To Мне кажется, что все эти состояния принадлежат OverlayContainer, и изменение состояния должно быть как можно более коротким и понятным. Определение состояний повсюду похоже на дублирование кода. Может мне нужна совсем другая архитектура?

Ответы [ 2 ]

0 голосов
/ 27 мая 2020

Чтобы сделать так, как вы хотите, используйте Binding:

struct OverlayContainer: View {

    @Binding var isLoading: Bool
    @Binding var isErrorShown: Bool

    func setIsLoading(isLoading: Bool) {
        self.isLoading = isLoading
        self.isErrorShown = !isLoading
    }

    func setIsErrorShown(isErrorShown: Bool) {
        self.isErrorShown = isErrorShown
        self.isLoading = !isErrorShown
    }

    var body: some View {
        Group {
            if(isErrorShown) {
                ErrorOverlay()
            }
            else if(isLoading) {
                LoadingOverlay()
            }
            else {
                EmptyView()
            }
        }
    }
}

struct Home: View {

    @State var isLoading = false
    @State var isErrorShown = false

    var body: some View {

        let overlayContainer = OverlayContainer(isLoading: $isLoading, isErrorShown: $isErrorShown)

        return HStack {
            // Some more content here

            Button(action: {
                overlayContainer.setIsLoading(isLoading: true)

            }) {
                Text("Start loading")
            }
            Button(action: {
                overlayContainer.setIsErrorShown(isErrorShown: true)
            }) {
                Text("Show error")
            }
        }.overlay(overlayContainer)
    }
}
0 голосов
/ 27 мая 2020

Вместо этого следует использовать Binding. Вот возможное решение.

struct OverlayContainer: View {
    @Binding var isLoading: Bool
    @Binding var isErrorShown: Bool

    var body: some View {
        Group {
            if(isErrorShown) {
                ErrorOverlay()
            }
            else if(isLoading) {
                LoadingOverlay()
            }
            else {
                EmptyView()
            }
        }
    }
}

struct Home: View {
    @State var isLoading: Bool = false
    @State var isErrorShown: Bool = false

    var body: some View {
        HStack {
            // Some more content here

            Button(action: {
                self.isLoading = true
            }) {
               Text("Start loading")
            }
            Button(action: {
                self.isErrorShown = true
            }) {
               Text("Show error")
            }
        }.overlay(OverlayContainer(isLoading: $isLoading, isErrorShown: $isErrorShown))
    }
}
...