Composing View with ViewModifier
Why we Composing View ?
As you know when we deal with view there are reusable component or expandable component, so we can expand or add new view component to current view.
Rather than we repeating code we can expand the view with Builder Pattern. Like you see on the image above when can expand Avatar view by composing them.
From just AvatarView we add RingView on top of that view then we can add ComposeView on top all view before.
So we create view level on our component.
How to Compose View ?
We can composing view using ViewModifer from SwiftUI. With that we can create view with component that we need.
Now we can go to the sample bellow
struct AvatarView: View {
let name: String
var body: some View {
Image(name)
.resizable()
.scaledToFit()
.clipShape(Circle())
}
}
struct RingView: ViewModifier {
@Binding var isLoading: Bool
private let gradient = LinearGradient(
gradient: Gradient(colors: [
Color.yellow,
Color.storyOrange,
Color.storyPink,
Color.storyPurple,
Color.storyDarkPurple
]),
startPoint: .bottomLeading,
endPoint: .topTrailing
)
func body(content: Content) -> some View {
content
.padding(.all, 4)
.overlay {
Circle()
.stroke(
gradient,
style: StrokeStyle(
lineWidth: 2,
dash: [isLoading ? 8 : 0]
)
)
.rotationEffect(.degrees(isLoading ? 360 : 0))
.animation(.default.speed(0.4).repeatForever(autoreverses: false), value: isLoading)
}
}
}
struct ComposeView: ViewModifier {
let systemName: String
let alignment: Alignment
let size: CGSize
init(
systemName: String = "plus",
alignment: Alignment = .bottomTrailing,
size: CGSize = CGSize(width: 24, height: 24)
) {
self.systemName = systemName
self.alignment = alignment
self.size = size
}
func body(content: Content) -> some View {
content
.overlay(alignment: alignment) {
Button {
} label: {
Circle()
.foregroundStyle(.blue)
.frame(width: size.width, height: size.height)
.overlay(Circle().stroke(.white, lineWidth: 2))
.overlay {
Image(systemName: systemName)
.font(.caption.bold())
.foregroundStyle(.white)
}
}
}
}
}
How to Use it ?
AvatarView(name: "img_avatar_1")
.modifier(RingView(isLoading: .constant(false)))
.modifier(ComposeView())
- First we call AvatarView
- Second we Add modifier with RingView
- Third we Add ComposeView to add plus button on top of All
What if I just need AvatarView ?
Just use AvatarView without adding modifier
AvatarView(name: "img_avatar_1")