ViewBuilder in SwiftUI
Updated:
ViewBuilder in SwiftUI
We can use a view builder to create closures in which we can create custom child views. In order to use the view builder and get the most out of it we actually use the view builder alongside generic types
import SwiftUI
// MARK: - VIEW
struct ViewBuilderBootCamp: View {
// MARK: - PROPERTY
// MARK: - BODY
var body: some View {
VStack {
HeaderViewRegular(title: "New Title", description: "Hello", iconName: "heart.fill")
HeaderViewRegular(title: "Another Title", description: nil, iconName: nil)
Spacer()
} //: VSTACK
}
}
// MARK: - EXTENSTION
struct HeaderViewRegular: View {
let title: String
let description: String?
let iconName: String?
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(title)
.font(.largeTitle)
.fontWeight(.semibold)
if let description = description {
Text(description)
.font(.callout)
}
if let iconName = iconName {
Image(systemName: iconName)
}
RoundedRectangle(cornerRadius: 5)
.frame(height: 2)
} //: VSTACK
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
}
Above method here is kind of getting annoying and not super efficient because we have custom logic for this description we’ve custom logic for this icon name what if we wanted to have 10 icons or more. So with this method we can actually just customize and add whatever we want into this view
If you want to be able to customize this view and put whatever we want inside of it we really need to pass a view into the view
To use @ViewBuilder to make customize aspects in Views
// MARK: - VIEW
struct ViewBuilderBootCamp: View {
// MARK: - PROPERTY
// MARK: - BODY
var body: some View {
VStack {
HeaderViewRegular(title: "New Title", description: "Hello", iconName: "heart.fill")
HeaderViewRegular(title: "Another Title", description: nil, iconName: nil)
HeaderViewGeneric(title: "Generic Tilte") {
HStack {
Text("Hi")
Image(systemName: "heart.fill")
} //: HSTACK
}
CustomHStack {
Text("Hi 1")
Text("Hi 2")
}
HStack {
Text("Hi 3")
Text("Hi 4")
}
Spacer()
} //: VSTACK
}
}
// MARK: - EXTENSTION
struct HeaderViewRegular: View {
let title: String
let description: String?
let iconName: String?
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(title)
.font(.largeTitle)
.fontWeight(.semibold)
if let description = description {
Text(description)
.font(.callout)
}
if let iconName = iconName {
Image(systemName: iconName)
}
RoundedRectangle(cornerRadius: 5)
.frame(height: 2)
} //: VSTACK
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
}
struct HeaderViewGeneric<Content:View>: View {
let title: String
let content: Content
init(title: String, @ViewBuilder content: () -> Content) {
self.title = title
self.content = content()
}
var body: some View {
VStack(alignment: .leading, spacing: 10) {
Text(title)
.font(.largeTitle)
.fontWeight(.semibold)
content
// if let description = description {
// Text(description)
// .font(.callout)
// }
// if let iconName = iconName {
// Image(systemName: iconName)
// }
//
RoundedRectangle(cornerRadius: 5)
.frame(height: 2)
} //: VSTACK
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
}
struct CustomHStack<Content:View>: View {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
HStack {
content
}
}
}
We can use @ViewBuilder instead of using it inside the init and we can actually just declare custom variables with the view builder attribute
struct LocalViewBuilder: View {
enum ViewType {
case one, two, three
}
let type: ViewType
@ViewBuilder private var headerSection: some View {
switch type {
case .one:
viewOne
case .two:
viewTwo
case .three:
viewThree
}
// if type == .one {
// viewOne
// } else if type == .two {
// viewTwo
// } else if type == .three {
// viewThree
// }
}
private var viewOne: some View {
Text("One!")
}
private var viewTwo: some View {
VStack {
Text("Two")
Image(systemName: "heart.fill")
}
}
private var viewThree: some View {
Image(systemName: "heart.fill")
}
var body: some View {
VStack {
headerSection
} //: VSTACK
}
}
/ MARK: - PREVIEW
struct ViewBuilderBootCamp_Previews: PreviewProvider {
static var previews: some View {
// ViewBuilderBootCamp()
LocalViewBuilder(type: .one)
}
}
🗃 Reference
SwiftUI Thinking - https://www.youtube.com/watch?v=EHhgjOt_KFA&list=PLwvDm4Vfkdphc1LLLjCaEd87BEg07M97y&index=6
Leave a comment