swift - 嵌入 HStack 时 PreferenceKey 不起作用 - SwiftUI
问题描述
以下代码是该问题的简化版本。
该Dimensions
视图采用任何视图组合,并显示使用 PreferenceKeys 组合的所有视图的宽度和高度。
如示例 1 所示:当在其中构建所有视图Dimensions,
时,它将工作并显示大小。
而在示例 2 和 3 中:如果视图是在外部构建的Dimensions,
,它将不起作用。
问题:当视图在外部构建时它会如何工作?谢谢。
在 Xcode 版本 12.5.1 (12E507) 上运行目标 iOS 14.5
这是代码:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
// Example 1
Dimensions {
HStack {
Text("Hello, world!")
.padding()
// HelloWorldVariable // <===== THIS WILL BREAK IT
// HelloWorldView() // <===== THIS WILL BREAK IT
}
}.background(Color.blue)
// Example 2
Dimensions {
HStack {
HelloWorldVariable // <===== THIS DOES NOT WORK in the HStack
}
}.background(Color.green)
// Example 3
Dimensions {
HStack {
HelloWorldView() // <===== THIS DOES NOT WORK in the HStack
}
}.background(Color.yellow)
}
}
private var HelloWorldVariable: some View {
Text("Hello, world!")
.padding()
}
}
// MARK:- HelloWorldView
struct HelloWorldView: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
// MARK:- Dimensions
struct Dimensions<Content: View>: View {
private var content: () -> Content
@State private var contentSize: CGSize = .zero
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
VStack {
content()
.background(ObserveViewDimensions())
VStack {
Text("contentWidth \(contentSize.width)")
Text("contentHeight \(contentSize.height)")
}
}
.onPreferenceChange(DimensionsKey.self, perform: { value in
self.contentSize = value
})
}
}
// MARK:- ObserveViewDimensions
struct ObserveViewDimensions: View {
var body: some View {
GeometryReader { geometry in
Color.clear.preference(key: DimensionsKey.self, value: geometry.size)
}
}
}
// MARK:- DimensionsKey
struct DimensionsKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
typealias Value = CGSize
}
// MARK:- Preview
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
解决方案
经过进一步调查,事实证明 Canvas (SwiftUI Previews) 在 Xcode 版本 12.5.1 (12E507) 中存在该错误。
原始代码适用于运行 iOS 14.8 的 iPhone X
有一种解决方法可以使其适用于 Canvas,方法是更改 PreferenceKey 以保存数组,如下面的代码所示。
灵感来自: https ://swiftui-lab.com/communicating-with-the-view-tree-part-1/
这是新代码:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
// Example 1
Dimensions {
HStack {
Text("Hello, world!")
.padding()
HelloWorldVariable
HelloWorldView()
}
}
}
}
private var HelloWorldVariable: some View {
Text("Hello, world!")
.padding()
}
}
// MARK:- HelloWorldView
struct HelloWorldView: View {
var body: some View {
Text("Hello, world!")
.padding()
}
}
// MARK:- Dimensions
struct Dimensions<Content: View>: View {
private var content: () -> Content
@State private var contentSize: CGSize = .zero
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
VStack {
content()
.background(ObserveViewDimensions())
VStack {
Text("contentWidth \(contentSize.width)")
Text("contentHeight \(contentSize.height)")
}
}
.onPreferenceChange(DimensionsKey.self, perform: { value in
// 4. Add this
DispatchQueue.main.async {
self.contentSize = value.first?.size ?? .zero
}
})
}
}
// MARK:- ObserveViewDimensions 3. // <== update this with the new data type
struct ObserveViewDimensions: View {
var body: some View {
GeometryReader { geometry in
Color.clear.preference(key: DimensionsKey.self, value: [ViewSizeData(size: geometry.size)])
}
}
}
// MARK:- DimensionsKey 2. // <== update this with the new data type
struct DimensionsKey: PreferenceKey {
static var defaultValue: [ViewSizeData] = []
static func reduce(value: inout [ViewSizeData], nextValue: () -> [ViewSizeData]) {
value.append(contentsOf: nextValue())
}
typealias Value = [ViewSizeData]
}
// MARK:- ViewSizeData 1 <==== Add This
struct ViewSizeData: Identifiable, Equatable, Hashable {
let id: UUID = UUID()
let size: CGSize
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
// MARK:- Preview
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
推荐阅读
- amazon-web-services - AWS Kinesis - Kinesis 代理不解析记录
- python-3.x - Python:将条目添加到 PyQt5 菜单栏列表
- shell - 使用 sox、shell 脚本将立体声音频文件转换为单声道
- android - 导航组件 Safe Args 通行证列表数据
- jquery - 如何遍历在 jQuery 中创建的元素以获取它们的文本值?
- excel - 如何在 Excel 中添加多个 countif 语句?
- css - Vuetify css override 改变组件的视图
- c# - 是否有没有 IV 的 AES-128 CBC 加密?
- azure-pipelines - 生产管道成功后,基于 Angular 的应用程序未选择生产环境配置
- python - 线性回归图