Reputation: 690
In my iOS App i want to place two views of the same width so that they fill the entire width of the parent view.
For this I use GeometryReader and it broke auto layout. But auto layout does not work and the height of this view is not calculated automatically. Height of TestView is not determined, so i cant add frame size manually...
Here's what it should look like (what i expect TestView):
This is what it looks like when I put a view on a list (CurrenciesView):
TestView.swift
struct TestView: View {
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0.0) {
Text("Name 1\n Test second name 2")
.font(.system(size: 18))
.fontWeight(.bold)
HStack {
Text("123")
Text(" + 5")
}
}
.padding(.horizontal, 12.0)
.padding(.vertical, 9.0)
.frame(width: geometry.size.width / 2)
.background(RoundedRectangle(cornerRadius: 8.0)
.foregroundColor(Color.blue
.opacity(0.2)))
VStack(alignment: .leading, spacing: 0.0) {
Text("Name 1")
.font(.system(size: 18))
.fontWeight(.bold)
HStack {
Text("123")
Text(" + 5")
}
}
.padding(.horizontal, 12.0)
.padding(.vertical, 9.0)
.frame(width: geometry.size.width / 2)
.background(RoundedRectangle(cornerRadius: 8.0)
.foregroundColor(Color.blue
.opacity(0.2)))
}
}
}
}
CurrenciesView.swift
struct CurrenciesView: View {
@State private var items: [Str] = (0..<5).map { i in
return Str(title: "Struct #\(i)")
}
var body: some View {
NavigationView {
List {
Section(header:
TestView().listRowInsets(EdgeInsets())
) {
ForEach(items) { item in
Text("asd")
}
}.clipped()
}
.navigationBarTitle("Section Name")
.navigationBarItems(trailing: EditButton())
}
}
}
Upvotes: 2
Views: 1519
Reputation: 54486
You can create a custom PreferenceKey
and a view that calculates it:
struct ViewSizeKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct ViewGeometry: View {
var body: some View {
GeometryReader { geometry in
Color.clear
.preference(key: ViewSizeKey.self, value: geometry.size)
}
}
}
Then, you can use them in your views. Note that you need to use @Binding
in the TestView
and @State private var headerSize
in the parent view. Otherwise the parent view won't be refreshed and the List won't re-calculate the header size properly.
struct CurrenciesView: View {
@State private var items: [String] = (0 ..< 5).map(String.init)
@State private var headerSize: CGSize = .zero
var body: some View {
NavigationView {
List {
Section(header:
TestView(viewSize: $headerSize)
) {
ForEach(items, id: \.self) {
Text($0)
}
}.clipped()
}
.navigationBarTitle("Section Name")
.navigationBarItems(trailing: EditButton())
}
}
}
struct TestView: View {
@Binding var viewSize: CGSize
var body: some View {
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0.0) {
Text("Name 1\n Test second name 2")
.font(.system(size: 18))
.fontWeight(.bold)
HStack {
Text("123")
Text(" + 5")
}
}
.padding(.horizontal, 12.0)
.padding(.vertical, 9.0)
.frame(width: viewSize.width / 2)
.background(RoundedRectangle(cornerRadius: 8.0)
.foregroundColor(Color.blue
.opacity(0.2)))
VStack(alignment: .leading, spacing: 0.0) {
Text("Name 1")
.font(.system(size: 18))
.fontWeight(.bold)
HStack {
Text("123")
Text(" + 5")
}
}
.padding(.horizontal, 12.0)
.padding(.vertical, 9.0)
.frame(width: viewSize.width / 2)
.background(RoundedRectangle(cornerRadius: 8.0)
.foregroundColor(Color.blue
.opacity(0.2)))
}
.frame(maxWidth: .infinity)
.background(ViewGeometry()) // calculate the view size
.onPreferenceChange(ViewSizeKey.self) {
viewSize = $0 // assign the size to `viewSize`
}
}
}
Upvotes: 1