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

Categories:

Updated:

Leave a comment