问题
I'm fiddling with a view layout - a graph - where I'm seeing how far I can get within an all-SwiftUI layout. Each of the component pieces are fine, but assembling the whole isn't working quite as I'd like. What I've found is that I can easily constrain sizing along a single stack axis - but not two axis at once (both vertical and horizontal).
I started to reach for AlignmentGuides, as I found you can align non-siblings with a custom guide. That will help my goal, but it doesn't solve the sizing part, which is the heart of this question:
Is there a way to constrain a view's size based on another, non-sibling, view?
A simplification of the structure is:
HStack {
CellOneView {
}
CellTwoView {
}
}
HStack {
CellThreeView {
}
CellFourView {
}
}
Which maps out to:
+-----+-----+
| 1 | 2 |
+-----+-----+
| 3 | 4 |
+-----+-----+
Is there a way to tell CellFour (which isn't in the same HStack as cell's 1 and 2) that I want it to constrain itself (and align) to the width of cell CellTwo?
This does not need to strictly be a grid view (example of grid view). There are really only three views that I care about in this case - the areas that roughly map to cell 1, cell 2, and cell 4. I want the heights of Cell 1 and Cell 2 to be the same (accomplished easily with the current HStack), and the widths of Cell 2 and Cell 4 to be the same - that's where I'm struggling.
回答1:
1.Getting the size
struct CellTwoView: View {
var body: some View {
Rectangle()
.background(
GeometryReader(content: { (proxy: GeometryProxy) in
Color.clear
.preference(key: MyPreferenceKey.self, value: MyPreferenceData(rect: proxy.size))
})
)
}
}
Explanation - Here I have get the size of the view from using background View ( Color.clear ) , I used this trick unless getting the size from CellTwoView itself ; 'cause of SwiftUI-View size is determined by the view itself If they have size ( parent cannot change the size like in UIKit ). so if I use GeometryReader with CellTwoView itself , then the GeometryReader takes as much as size available in the parent of CellTwoView. - > reason -> GeometryReader depends on their parent size. (actually this is another topic and the main thing in SwiftUI)
Key ->
struct MyPreferenceKey: PreferenceKey {
static var defaultValue: MyPreferenceData = MyPreferenceData(size: CGSize.zero)
static func reduce(value: inout MyPreferenceData, nextValue: () -> MyPreferenceData) {
value = nextValue()
}
typealias Value = MyPreferenceData
}
Value (and how it is handle when preference change) ->
struct MyPreferenceData: Equatable {
let size: CGSize
//you can give any name to this variable as usual.
}
2. Applying the size to another view
struct ContentView: View {
@State var widtheOfCellTwoView: CGFloat = .zero
var body: some View {
VStack {
HStack {
CellOneView()
CellTwoView ()
.onPreferenceChange(MyPreferenceKey.self) { (prefereneValue) in
self.widtheOfCellTwoView = prefereneValue.size.width
}
}
HStack {
CellThreeView ()
CellFourView ()
.frame(width: widtheOfCellTwoView)
}
}
}
}
回答2:
As you already started using alignment guides it is possible to with this instrument. Here is possible approach (for your scratchy example):
@State private var width: CGFloat = 10 // < initial value does not much matter
...
HStack {
CellOneView {
}
CellTwoView {
}
.alignmentGuide(VerticalAlignment.center, computeValue: { d in
// for simplicity of demo skipped checking for equality
DispatchQueue.main.async { // << must be async
self.width = d.width // << set limit
}
return d[VerticalAlignment.center]
})
}
HStack {
CellThreeView {
}
CellFourView {
}
.frame(width: self.width) // << apply limit, updated right in next loop
}
来源:https://stackoverflow.com/questions/60726589/with-swiftui-is-there-a-way-to-constrain-a-views-size-to-another-non-sibling-v