Usually you want to either have a common behaviour or a common style.
1) To have a common behaviour: composition with generics
Let’s say we need to create a BgView
which is a View
with a full screen image as background. We want to reuse BgView
whenever we want. You can design this situation this way:
struct BgView<Content>: View where Content: View {
private let bgImage = Image.init(systemName: "m.circle.fill")
let content: Content
var body : some View {
ZStack {
bgImage
.resizable()
.opacity(0.2)
content
}
}
}
You can use BgView
wherever you need it and you can pass it all the content you want.
//1
struct ContentView: View {
var body: some View {
BgView(content: Text("Hello!"))
}
}
//2
struct ContentView: View {
var body: some View {
BgView(content:
VStack {
Text("Hello!")
Button(action: {
print("Clicked")
}) {
Text("Click me")
}
}
)
}
}
2) To have a common behaviour: composition with @ViewBuilder closures
This is probably the Apple preferred way to do things considering all the SwiftUI APIs. Let’s try to design the example above in this different way
struct BgView<Content>: View where Content: View {
private let bgImage = Image.init(systemName: "m.circle.fill")
private let content: Content
public init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body : some View {
ZStack {
bgImage
.resizable()
.opacity(0.2)
content
}
}
}
struct ContentView: View {
var body: some View {
BgView {
Text("Hello!")
}
}
}
This way you can use BgView
the same way you use a VStack
or List
or whatever.
3) To have a common style: create a view modifier
struct MyButtonStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color.red)
.foregroundColor(Color.white)
.font(.largeTitle)
.cornerRadius(10)
.shadow(radius: 3)
}
}
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Button(action: {
print("Button1 clicked")
}) {
Text("Button 1")
}
.modifier(MyButtonStyle())
Button(action: {
print("Button2 clicked")
}) {
Text("Button 2")
}
.modifier(MyButtonStyle())
Button(action: {
print("Button3 clicked")
}) {
Text("Button 3")
}
.modifier(MyButtonStyle())
}
}
}
These are just examples but usually you’ll find yourself using one of the above design styles to do things.
EDIT: a very useful link about @functionBuilder (and therefore about @ViewBuilder) https://blog.vihan.org/swift-function-builders/