Отображение и скрытие представлений в SwiftUI

#ios #swift #swiftui

Вопрос:

Я хочу показать представление во время выполнения задач, как еще я мог бы вызвать SwiftView для отображения без использования условных обозначений, основанных на bool состояния? В принципе, я хочу избежать того, чтобы это было жестко закодировано ниже для каждого файла, для которого потребуется представление загрузки…

 struct ContentView: View {
    @State var doIWantThisViewToShow: Bool = false
    
    var body: some View {
        VStack {
            Button("Show/Hide MyView") {
                doIWantThisViewToShow.toggle()
            }
            if doIWantThisViewToShow {
                MyView()
                    .padding()
            }
        }
    }
}
 

Комментарии:

1. Вы имеете в виду, что у вас будет список файлов (например, в ForEach ), и вы не хотите писать по одному @State для каждого из них? Вы можете абстрагировать его в собственный компонент, так что вы напишете его только @State один раз, но будете повторно использовать компонент ( View ) снова и снова.

2. @jnpdx В принципе, лучшим примером является вход в приложение в Firebase. Я должен показать представление загрузки, но я не хочу писать его как условие для каждой «страницы» в моем приложении, привязанной к bool. Потому что, если я сделаю это таким образом, мне придется писать на каждой странице. если doIWantThisViewToShow { MyView() .заполнение() }

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

4. @jnpdx спасибо за ясность, но каково самое верхнее родительское представление или как это будет выглядеть в коде?

5. Это выглядит точно так же, как у вас сейчас, при условии, что вы делаете все остальное дочерним представлением (существует внутри) MyView .

Ответ №1:

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

  1. Пользовательское значение среды, чтобы избежать передачи @Binding
  2. Многократно используемые эффекты
  3. Может быть, протокол, если ваше состояние сложное (возможно, нет)

Изменение иерархии представлений

Пользователь EnvironmentValue передает состояние дочерним представлениям. Это избавит вас от передачи @Binding через представления, которые могут не использовать значение.

Имейте в виду, что это односторонняя трансляция сверху вниз. В отличие от @Binding, дети не могут изменять родительское состояние. (Но они могут изменить знания своих собственных детей об этом состоянии.)

Установить в родительском

 @State var isHovered = false

var parent: some View {
///
  .environment(.parentIsHovered, isHovered)
}

 

Наблюдайте за ребенком

 @Environment(.parentIsHovered) var parentIsHovered

var child: some View {
///
   .grayscale(parentIsHovered ? 0 : 0.9)
   .animation(.easeInOut, value: parentIsHovered)
}

 

Определять

 private struct ParentIsHoveredKey: EnvironmentKey {
    static let defaultValue: Bool = false
}


extension EnvironmentValues {
    
    var parentIsHovered: Bool {
        get { return self[ParentIsHoveredKey] }
        set { self[ParentIsHoveredKey] = newValue }
    }
}
 

Многократно используемые эффекты

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

Приведенный ниже пример демонстрирует это, связывая .animation API с accessibilityReduceMotion .

 // view
    .animate(.easeOut(duration: .fast), value: isLoading)

 
 
extension View {
    func animate<E: Equatable>(_ animation: Animation?, value: E) -> some View  {
        self.modifier(AccessibleAnimationModifier(animation, for: value))
    }
}

struct AccessibleAnimationModifier<E: Equatable>: ViewModifier {
    
    @Environment(.accessibilityReduceMotion) var reduceMotion
    
    init(_ animation: Animation? = .default, for value: E) {
        self.animation = animation
        self.value = value
    }
    
    var animation: Animation?
    var value: E
    
    func body(content: Content) -> some View {
        content
            .animation(reduceMotion ? .none : animation, value: value)
    }
}

 

Реагирование на состояние загрузки

Если вы не обрабатываете состояние загрузки с помощью какого-либо наблюдаемого класса, вам необходимо сохранить это состояние в своем представлении с помощью @State.

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

Псевдокод ниже определяет источник перетаскивания протокола с функциями, возвращающими NSItemProvider. Расширение предоставляет реализации по умолчанию, которые может вызывать представление или виртуальная машина.

 protocol DragSource {
    
    func makeDraggableThing1(/// content   logic objects) -> NSItemProvider

}

extension DragSource {
    
    func makeDraggableThing1(///) -> NSItemProvider {
        /// Default work I only want to edit once in the app
    }
}