With SwiftUI, is there a way to constrain a view's size to another non-sibling view?

妖精的绣舞 提交于 2021-01-27 13:09:59

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!