SwiftUI - ContentUnavailableView
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 SwiftUI의 ContentUnavailableView에 대해 알아보겠습니다 🙋🏻
ContentUnavailableView?
해당 뷰 구조체는 사용자가 앱 콘텐츠를 사용할 수 없을때 표시되는 레이블과 추가 콘텐츠로 구성된 인터페이스입니다.
즉, 데이터가 없을때 없다고 보여주는 뷰인데 이걸 직접 SwiftUI에서 쓰기 쉽게 제공해주는 역할을 하죠.
다만, 어느정도 틀 규격이 있어서 완전히 다른 뷰를 보여주려면 직접 만들어야 합니다 🥲
그래도 한번 알아보시죠!
해당 인터페이스는 iOS 17에서 나와서 17 이상부터 사용할 수 있고, 정의는 이렇습니다.
@MainActor @preconcurrency
struct ContentUnavailableView<Label, Description, Actions> where Label : View, Description : View, Actions : View
추후 Swift6 업데이트 시 Strict Concurrency에 대응하기 위해 @preconcurrency를 사용한것이 눈에 띄네요 👀
Label과 Description, Actions을 받아올 수 있는 간단한 뷰입니다.
그럼 한번 실제로 어떻게 사용하는지 알아볼까요?
// View
import SwiftUI
struct ContentView: View {
@ObservedObject private var viewModel = ContactsViewModel()
var body: some View {
NavigationStack {
List {
ForEach(viewModel.searchResults) { contact in
NavigationLink {
ContactsView(contact: contact)
} label: {
Text(contact.name)
}
}
}
.navigationTitle("Contacts")
.searchable(text: $viewModel.searchText)
.overlay {
if viewModel.searchResults.isEmpty {
ContentUnavailableView.search
}
}
}
}
}
struct ContactsView: View {
var contact: Contact
var body: some View {
Text("Details for \(contact.name)")
}
}
// Model
class ContactsViewModel: ObservableObject {
@Published var searchText = ""
@Published var searchResults: [Contact] = []
}
struct Contact: Identifiable {
let id = UUID()
let name: String
}
간단히 공식문서에서 디벨롭을 해봤어요.
연락처를 나타내고 디테일로 들어가는 간단한 스택 구조의 뷰입니다.
여기서 서치 기능을 searchable로 넣었어요.
즉, 연락처를 검색할 수도 있죠.
그런데, 검색된 연락처가 없거나 아예 연락처가 없는 경우가 있을거잖아요?
그럴때, ContentUnavailableView를 사용해 데이터가 없을때 제공할 안내 뷰를 사용합니다.
오버레이로 올리면 되는데요.
사용 코드를 보면 간단하죠?
데이터가 비어있을때, ContentUnavailableView에서 내부적으로 지정한 하나의 static 값인 search가 있어요.
즉, 대부분 이 뷰를 사용하는 경우가 서치 기능과 어우러진다고 유추할 수 있습니다.
그럼 어떻게 나오는지 볼까요?
이런식으로, 기본 search 값에 구현된 이미지, 타이틀, 디스크립션이 나타나는걸 볼 수 있습니다.
추가로, search 값에 text를 넣어서 타이틀을 변경할 수 있어요.
.overlay {
if viewModel.searchResults.isEmpty {
ContentUnavailableView.search(text: viewModel.searchText)
}
}
이렇게 우리는 현재 검색한 텍스트가 없다는걸 표시하기 위해 text로 해당 searchText 값을 넣을 수 있습니다.
그럼 이렇게 나오게 됩니다.
생각보다 유용하게 쓸 수 있겠죠?
이렇게 ContentUnavailableView에서 미리 만들어놓은 search를 사용할 수도 있겠지만, 커스텀하게 만들수도 있어요 당연히!!
기본적으로 이니셜라이저를 보면 레이블부터 액션까지 넣을 수 있습니다.
nonisolated
init(
@ViewBuilder label: () -> Label,
@ViewBuilder description: () -> Description = { EmptyView() },
@ViewBuilder actions: () -> Actions = { EmptyView() }
)
실제 사용은 이렇게 해볼 수 있어요.
.overlay {
if viewModel.searchResults.isEmpty {
ContentUnavailableView {
Label(
"No Results for \(viewModel.searchText)",
systemImage: "magnifyingglass"
)
} description: {
Text("Check the spelling or try a new search.")
}
}
}
Label에는 이미지와 텍스트를 넣을 수 있죠.
그리고 디스크립션도 정해진게 아닌 커스텀하게 사용할 수 있어요.
그럼 아래처럼 지정한 커스텀으로 나오게 되겠죠?
즉, 크게 이미지, 타이틀, 디스크립션을 커스텀하게 만들 수 있습니다.
그리고 action도 있었죠?
만약 필요하면 리프레쉬 등 사용자 액션을 넣어줄 수 있는 뷰 및 기능을 같이 포함할 수 있어요.
.overlay {
if viewModel.searchResults.isEmpty {
ContentUnavailableView(
label: {
Label(
"No Results for \(viewModel.searchText)",
systemImage: "magnifyingglass"
)
},
description: {
Text("Check the spelling or try a new search.")
},
actions: {
Button(action: {}) {
Text("Refresh")
}
}
)
}
}
이렇게 사용할 수 있겠죠?
액션에 적절히 담아 다시 서칭을 하거나 다른 뷰로 이동하거나 로직을 넣어줄 수 있습니다.
여기까지가 커스텀하게 쓸 수 있는 부분일것 같아요.
되게 쉽게 사용할 수 있을것 같지 않나요?
iOS 17에서부터 사용할 수 있기에 회사에서 사용하기에 무리가 있는 부분들도 있고 또 완전 커스텀하게 사용되는 환경이 많은 프로젝트에서는 안맞을 수 있지 않을까 싶어요!
그럴때는 다 커스텀하게 하는것도 SwiftUI에서는 쉽고 빠른 방법일 수 있습니다 ☺️
마무리
또 iOS 17에서 새롭게 나온 SwiftUI 기능에 대해 알아봤습니다ㅎㅎ
정말 버전업 될때마다 많은것들이 나와서 재밌네요!