SwiftUI列表空状态视图/修改器

hfyxw5xn  于 2023-11-16  发布在  Swift
关注(0)|答案(7)|浏览(154)

我想知道当列表的数据源为空时,如何在列表中提供一个空的状态视图。下面是一个例子,我必须将其 Package 在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: [])
    }

}

字符串

ycl3bljg

ycl3bljg1#

我很喜欢在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, looks like there's no data...")
                }
            })
        }
    }
}

字符串
它的优点是很好地居中&如果你使用更大的占位符与图像等,他们将填补相同的区域作为列表。

thtygnil

thtygnil2#

其中一个解决方案是使用@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)")
        }
    }
}

字符串

nom7f22z

nom7f22z3#

您可以创建一个自定义修饰符,在列表为空时替换占位符视图。如下使用:

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())))
    }
}

2skhul33

2skhul334#

我尝试了@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)
        }
    }  
}

字符串

kuhbmx9i

kuhbmx9i5#

你可以像这样使用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 = []
                    }
                }
            }
        }
    }
}


的数据

jtw3ybtb

jtw3ybtb6#

在2021年,苹果没有提供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

cngwdvgl

cngwdvgl7#

对于iOS 17,您可以使用ContentUnavailable View

ContentUnavailableView(
   "Favourite list empty",
   systemImage: "star",
   description: "Add new item to favourite list")
)

字符串
如果你愿意,你可以添加一个foregroundStyle,所有的文本和图像都将有一个不同的颜色从相同的颜色:

ContentUnavailableView(
   "Favourite list empty",
   systemImage: "star",
   description: "Add new item to favourite list")
)
.foregroundStyle(.blue)


如果你想添加一个动作,你可以做下面的代码:

ContentUnavailableView {
     Label("Favourite list empty", systemImage: "star")
} description: {
     Text("Add new item to favourite list")
} actions: {
     Button("Tap here") {
        // add the action here
     }
     .buttonStyle(.borderedProminent)
}

相关问题