Skip to content

Instantly share code, notes, and snippets.

@jemmons
Created December 7, 2019 15:54
Show Gist options
  • Save jemmons/2d0f80ebed382f07adb6cc9f6a9afa6d to your computer and use it in GitHub Desktop.
Save jemmons/2d0f80ebed382f07adb6cc9f6a9afa6d to your computer and use it in GitHub Desktop.
SwiftUI view that will either ForEach over data or display given view if empty.
import SwiftUI
struct ForEachOrEmpty<Data, ID, ForEachContent, EmptyContent>: View where Data: RandomAccessCollection, ID: Hashable, ForEachContent: View, EmptyContent: View {
let data: Data
let forEachBuilder: (Data.Element) -> ForEachContent
let emptyBuilder: () -> EmptyContent
let identityKeyPath: KeyPath<Data.Element, ID>
init(_ data: Data, id: KeyPath<Data.Element, ID>, @ViewBuilder each: @escaping (Data.Element)->ForEachContent, @ViewBuilder empty: @escaping () -> EmptyContent) {
self.data = data
identityKeyPath = id
forEachBuilder = each
emptyBuilder = empty
}
var body: some View {
if data.isEmpty {
return AnyView(emptyBuilder())
} else {
return AnyView(ForEach(data, id: identityKeyPath, content: forEachBuilder))
}
}
}
extension ForEachOrEmpty where ID == Data.Element.ID, Data.Element: Identifiable {
init(_ data: Data, @ViewBuilder each: @escaping (Data.Element)->ForEachContent, @ViewBuilder empty: @escaping () -> EmptyContent) {
self.init(data, id: \.id , each: each, empty: empty)
}
}
struct ForEachOrEmpty_Previews: PreviewProvider {
static let someNumbers = [1,2,3]
static let noNumbers: [Int] = []
struct IDString: Identifiable {
let value: String
var id: String { value }
init(_ value: String) { self.value = value }
}
static let someLetters = [IDString("a"), IDString("b"), IDString("c")]
static let noLetters: [IDString] = []
static var previews: some View {
Group {
VStack {
Divider()
ForEachOrEmpty(someNumbers, id: \.self, each: { number in
VStack {
Text("number \(number)")
Divider()
}
}, empty: {
VStack {
Text("None")
Divider()
}
})
}
VStack {
Divider()
ForEachOrEmpty(noNumbers, id: \.self, each: { number in
VStack {
Text("number \(number)")
Divider()
}
}, empty: {
VStack {
Text("None")
Divider()
}
})
}
VStack {
Divider()
ForEachOrEmpty(someLetters, each: { letter in
VStack {
Text(letter.value)
Divider()
}
}, empty: {
VStack {
Text("None")
Divider()
}
})
}
VStack {
Divider()
ForEachOrEmpty(noLetters, each: { letter in
VStack {
Text(letter.value)
Divider()
}
}, empty: {
VStack {
Text("None")
Divider()
}
})
}
}.previewLayout(.sizeThatFits)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment