SwiftUI 列表空状态 view/modifier
SwiftUI list empty state view/modifier
我想知道当列表的数据源为空时,如何在列表中提供一个空的状态视图。下面是一个示例,我必须将其包装在 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: [])
}
}
其中一个解决方案是使用 @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)")
}
}
}
我尝试了@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)
}
}
}
我非常喜欢使用附加到 List
的 overlay
,因为它是一个非常简单、灵活的修饰符:
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...")
}
})
}
}
}
它的优点是可以很好地居中,如果您使用带有图像等的较大占位符,它们将填充与列表相同的区域。
您可以像这样制作 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 = []
}
}
}
}
}
}
您可以创建自定义修饰符,在列表为空时替换占位符视图。像这样使用它:
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())))
}
}
2021 年 Apple 未提供开箱即用的 List
占位符。
在我看来,制作 placeholder 的最佳方法之一是创建自定义 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
我想知道当列表的数据源为空时,如何在列表中提供一个空的状态视图。下面是一个示例,我必须将其包装在 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: [])
}
}
其中一个解决方案是使用 @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)")
}
}
}
我尝试了@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)
}
}
}
我非常喜欢使用附加到 List
的 overlay
,因为它是一个非常简单、灵活的修饰符:
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...")
}
})
}
}
}
它的优点是可以很好地居中,如果您使用带有图像等的较大占位符,它们将填充与列表相同的区域。
您可以像这样制作 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 = []
}
}
}
}
}
}
您可以创建自定义修饰符,在列表为空时替换占位符视图。像这样使用它:
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())))
}
}
2021 年 Apple 未提供开箱即用的 List
占位符。
在我看来,制作 placeholder 的最佳方法之一是创建自定义 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