【问题标题】:SwiftUI list empty state view/modifierSwiftUI 列出空状态视图/修改器
【发布时间】:2020-10-05 11:22:11
【问题描述】:

我想知道当列表的数据源为空时如何在列表中提供空状态视图。下面是一个示例,我必须将其包装在 if/else 语句中。有没有更好的替代方案,或者有没有办法在List 上创建一个修饰符,这将使这成为可能,即List.emptyView(Text("No data available..."))

import SwiftUI

struct EmptyListExample: View {

    var objects: [Int]

    var body: some View {
        VStack {
            if objects.isEmpty {
                Text("Oops, loos like there's no data...")
            } else {
                List(objects, id: \.self) { obj in
                    Text("\(obj)")
                }
            }
        }
    }

}

struct EmptyListExample_Previews: PreviewProvider {

    static var previews: some View {
        EmptyListExample(objects: [])
    }

}

【问题讨论】:

  • if 有什么不好?无论如何,您将使用条件来检查数据是否存在。

标签: swift list swiftui empty-list


【解决方案1】:

其中一个解决方案是使用@ViewBuilder

struct EmptyListExample: View {
    var objects: [Int]

    var body: some View {
        listView
    }

    @ViewBuilder
    var listView: some View {
        if objects.isEmpty {
            emptyListView
        } else {
            objectsListView
        }
    }

    var emptyListView: some View {
        Text("Oops, loos like there's no data...")
    }

    var objectsListView: some View {
        List(objects, id: \.self) { obj in
            Text("\(obj)")
        }
    }
}

【讨论】:

    【解决方案2】:

    我非常喜欢为此使用附加到Listoverlay,因为它是一个非常简单、灵活的修饰符:

    struct EmptyListExample: View {
    
        var objects: [Int]
    
        var body: some View {
            VStack {
                List(objects, id: \.self) { obj in
                    Text("\(obj)")
                }
                .overlay(Group {
                    if objects.isEmpty {
                        Text("Oops, loos like there's no data...")
                    }
                })
            }
        }
    }
    

    它的优点是可以很好地居中,如果您在图像等中使用较大的占位符,它们将填充与列表相同的区域。

    【讨论】:

      【解决方案3】:

      我尝试了@pawello2222 的方法,但是如果传递的对象的内容从空(0)变为非空(> 0),则视图不会重新呈现,反之亦然,但如果对象的内容是永远不为空。

      以下是我一直以来的工作方法:

      struct SampleList: View {
      
          var objects: [IdentifiableObject]
      
          var body: some View {
          
              ZStack {
              
                  Empty() // Show when empty
              
                  List {
                      ForEach(objects) { object in
                          // Do something about object
                      }
                  }
                  .opacity(objects.isEmpty ? 0.0 : 1.0)
              }
          }  
      }  
      

      【讨论】:

        【解决方案4】:

        您可以像这样制作ViewModifier 来显示空视图。另外,请使用View 扩展名以方便使用。

        这里是演示代码,

        //MARK: View Modifier
        struct EmptyDataView: ViewModifier {
            let condition: Bool
            let message: String
            func body(content: Content) -> some View {
                valideView(content: content)
            }
            
            @ViewBuilder
            private func valideView(content: Content) -> some View {
                if condition {
                    VStack{
                        Spacer()
                        Text(message)
                            .font(.title)
                            .foregroundColor(Color.gray)
                            .multilineTextAlignment(.center)
                        Spacer()
                    }
                } else {
                    content
                }
            }
        }
        
        //MARK: View Extension
        extension View {
            func onEmpty(for condition: Bool, with message: String) -> some View {
                self.modifier(EmptyDataView(condition: condition, message: message))
            }
        }
        

        示例(如何使用)

        struct EmptyListExample: View {
            
            @State var objects: [Int] = []
            
            var body: some View {
                NavigationView {
                    List(objects, id: \.self) { obj in
                        Text("\(obj)")
                    }
                    .onEmpty(for: objects.isEmpty, with: "Oops, loos like there's no data...") //<--- Here
                    .toolbar {
                        ToolbarItemGroup(placement: .navigationBarTrailing) {
                            Button("Add") {
                                objects = [1,2,3,4,5,6,7,8,9,10]
                            }
                            Button("Empty") {
                                objects = []
                            }
                        }
                    }
                }
            }
        }
        

        【讨论】:

        • onEmpty 的名称可能会引起混淆。所有以on 开头的内置 SwiftUI 修饰符均由运行时事件触发。
        【解决方案5】:

        您可以创建一个自定义修饰符,当您的列表为空时替换占位符视图。像这样使用它:

        List(items) { item in
            Text(item.name)
        }
        .emptyPlaceholder(items) {
            Image(systemName: "nosign")
        }
        

        这是修饰符:

        struct EmptyPlaceholderModifier<Items: Collection>: ViewModifier {
            let items: Items
            let placeholder: AnyView
        
            @ViewBuilder func body(content: Content) -> some View {
                if !items.isEmpty {
                    content
                } else {
                    placeholder
                }
            }
        }
        
        extension View {
            func emptyPlaceholder<Items: Collection, PlaceholderView: View>(_ items: Items, _ placeholder: @escaping () -> PlaceholderView) -> some View {
                modifier(EmptyPlaceholderModifier(items: items, placeholder: AnyView(placeholder())))
            }
        }
        

        【讨论】:

          【解决方案6】:

          2021 年,Apple 没有提供开箱即用的 List 占位符。

          在我看来,制作占位符的最佳方法之一是创建自定义ViewModifier

          struct EmptyDataModifier<Placeholder: View>: ViewModifier {
          
              let items: [Any]
              let placeholder: Placeholder
          
              @ViewBuilder
              func body(content: Content) -> some View {
                  if !items.isEmpty {
                      content
                  } else {
                      placeholder
                  }
              }
          }
          
          struct ContentView: View {
          
              @State var countries: [String] = [] // Data source
          
              var body: some View {
                  List(countries) { country in
                      Text(country)
                          .font(.title)
                  }
                  .modifier(EmptyDataModifier(
                      items: countries,
                      placeholder: Text("No Countries").font(.title)) // Placeholder. Can set Any SwiftUI View
                  ) 
              }
          }
          

          通过扩展也可以稍微改进解决方案:

          extension List {
              
              func emptyListPlaceholder(_ items: [Any], _ placeholder: AnyView) -> some View {
                  modifier(EmptyDataModifier(items: items, placeholder: placeholder))
              }
          }
          
          struct ContentView: View {
          
              @State var countries: [String] = [] // Data source
          
              var body: some View {
                  List(countries) { country in
                      Text(country)
                          .font(.title)
                  }
                  .emptyListPlaceholder(
                      countries,
                      AnyView(ListPlaceholderView()) // Placeholder 
                  )
              }
          }
          

          如果您对其他方式感兴趣可以阅读the article

          【讨论】:

            猜你喜欢
            • 2022-11-13
            • 2019-12-17
            • 2021-05-04
            • 1970-01-01
            • 1970-01-01
            • 2020-06-12
            • 1970-01-01
            • 1970-01-01
            • 2021-08-21
            相关资源
            最近更新 更多