No more empty screens
Do you know that bad feeling when your backend is broken or API returns empty array of nothing? I think the worst things are when you don’t inform the user about “Something is wrong” or “For your filter is there no results” and he still waiting for something.
For these reasons I tried to create PlaceholderView
to inform the user about some error, response, or whatever else.
Tip: Maybe InfoView
is little bit better name, but in my App exist screen with this name. EmptyView
is reserved by Apple and ErrorView
sounds specific only for some cases.
Some cases in the App require a message, other for example errors require an image in red color and other just only image. So, I decided to separate this task into two steps:
- Create universal
PlaceholderView
independent on content - Create
PlaceholderViewType
, content forPlaceholderView
1. Universal PlaceholderView
However, this article is about PlaceholderView
, this part of code is very short. Just wrap content in the middle and set a placeholder background.
struct PlaceholderView: View {
let type: PlaceholderType
var body: some View {
type.content
.multilineTextAlignment(.center)
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.gray.opacity(0.1))
}
}
2. Content for PlaceholderView
Thanks to AnyView
we can wrap any content to our PlaceholderView
struct PlaceholderType {
var content: AnyView
}
Finally, we can with extension PlaceholderType
prepare content easily and fast.
extension PlaceholderType {
/// Placeholder type with `Text` element
/// - Parameter text: `Text` element string
static func text(_ text: String) -> PlaceholderType {
PlaceholderType(content: AnyView(Text(text)))
}
/// Placeholder type with `Image` element
/// - Parameter image: Name of image in Assets
static func image(_ image: String) -> PlaceholderType {
PlaceholderType(content: AnyView(Image(image)))
}
/// Placeholder type with witm `Image` and `Text` elements
/// - Parameters:
/// - imageName: Name of image in Assets
/// - color: Temple image color, if is not set, image will rendering with original colors
/// - text: `Text` element string
static func image(_ imageName: String, color: Color? = nil, text: String) -> PlaceholderType {
let image: AnyView
if let color = color {
image = AnyView(
Image(imageName)
.renderingMode(.template)
.foregroundColor(color)
)
} else {
image = AnyView(Image(imageName))
}
return PlaceholderType(content:
AnyView(
VStack {
image
Text(text)
}
)
)
}
}
Info: In the first solution of my PlaceholderView
I used enum
, but in the future I decide change it to struct
. Now you can extend it and modify it very easily from any part of your app.
We are in the final
Now we can use this helper view to inform the user about the current state.
struct PlaceholderView_Previews: PreviewProvider {
static var previews: some View {
VStack {
PlaceholderView(type: .text("No more data in this world!"))
PlaceholderView(type: .image("city"))
PlaceholderView(type: .image("city", text: "Unrecognized city name"))
PlaceholderView(type: .image("city", color: .red, text: "Something is wrong!"))
}
}
}