I have a view that represents a row in a cell that looks like this
This works well but the three horizontal elements (Image, Title/Subtitle, Image) are hardcoded to their respective types.
I would like to have a generic ThreeItemView
that can takes 3 Views
of any type and arrange them as shown. This would allow me to reuse the same container layout with any other views types.
I created a view that takes three @ViewBuilders
:
import Foundation
import SwiftUI
struct ThreeItemView<Start: View, Main: View, End: View>: View {
let start: () -> Start
let main: () -> Main
let end: () -> End
init(@ViewBuilder start: @escaping() -> Start,
@ViewBuilder main: @escaping() -> Main,
@ViewBuilder end: @escaping() -> End) {
self.start = start
self.main = main
self.end = end
}
var body: some View {
return HStack {
start()
main()
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
end()
}
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 60, alignment: .leading)
}
}
struct ThreeItemContainer_Previews: PreviewProvider {
static var previews: some View {
ThreeItemView(start: {
Image(systemName: "envelope.fill")
}, main: {
Text("Main")
}, end: {
Image(systemName: "chevron.right")
})
}
}
This works as expected but the API is a bit .. cumbersome. What would be a way to make the usage of the ThreeItemView
easier?
The @ViewBuilder attribute is one of the few result builders available for you to use in SwiftUI. You typically use it to create child views for a specific SwiftUI view in a readable way without having to use any return keywords.
@ViewBuilder is a kind of result builder that's specifically designed to help create child views. Result builders create functions that build a result from a sequence of elements. SwiftUI uses this in its own native views, controls and components. It also uses this in the body to compose your views.
Save this question. Show activity on this post. In the source code, ViewBuilder max parameters was limited to 10.
If you mean notation like the following
ThreeItemView {
Start {
Image(systemName: "envelope.fill")
}
Main {
Text("Main")
}
End {
Image(systemName: "chevron.right")
}
}
then find below modified your module
typealias Start<V> = Group<V> where V:View
typealias Main<V> = Group<V> where V:View
typealias End<V> = Group<V> where V:View
struct ThreeItemView<V1, V2, V3>: View where V1: View, V2: View, V3: View {
private let content: () -> TupleView<(Start<V1>, Main<V2>, End<V3>)>
init(@ViewBuilder _ content: @escaping () -> TupleView<(Start<V1>, Main<V2>, End<V3>)>) {
self.content = content
}
var body: some View {
let (start, main, end) = self.content().value
return HStack {
start
main
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
end
}
.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 60, alignment: .leading)
}
}
struct ThreeItemContainer_Previews: PreviewProvider {
static var previews: some View {
ThreeItemView {
Start {
Image(systemName: "envelope.fill")
}
Main {
Text("Main")
}
End {
Image(systemName: "chevron.right")
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With