问题描述
在SwiftUI中,使用HStack + ForEach遍历内容,发现视图内容不显示。
例如,我想要实现的效果为:

在Xcode中使用HStack + ForEach没有显示国旗内容。
HStack {
ForEach(countrys,id: \.self) { country in
Image("\(country)")
.resizable()
.scaledToFit()
Text(LocalizedStringKey(country))
.font(.footnote)
}
}

问题排查
当我给Image照片添加frame时,国旗才显示:
HStack {
ForEach(countrys,id: \.self) { country in
Image("\(country)")
.resizable()
.scaledToFit()
.frame(width: 100)
Text(LocalizedStringKey(country))
.font(.footnote)
}
}

同理,当给Text添加frame后,文字也会显示出来。
Text(LocalizedStringKey(country))
.font(.footnote)
.frame(minWidth: 100)

但是这里存在的一个问题就是,因为我的应用需要实现三十余种语言的本地化,某些语言的本地化名称较长,就会导致固定的frame并不能解决我的问题。
因此还需要寻找其他的解决方案。
解决方案
1、使用fixedSize()
明确Image设置frame,使用 fixedSize() 保障Text不被压缩。
HStack {
ForEach(countrys,id: \.self) { country in
Image("\(country)")
.resizable()
.scaledToFit()
.frame(width: 100)
Text(LocalizedStringKey(country))
.font(.footnote)
.fixedSize()
}
}

fixedSize() 可以让 Text 保持原本的尺寸,不受父视图的压缩影响。
2、使用LazyHStack加载视图
LazyHStack {
ForEach(countrys,id: \.self) { country in
Image("\(country)")
.resizable()
.scaledToFit()
.frame(width: 100)
Text(LocalizedStringKey(country))
.font(.footnote)
}
}
.frame(width: width,height: 30)

HStack 会立即将所有子视图加载到内存中,并计算它们的尺寸,然后在 HStack 的布局规则下进行布局。
如果没有明确的 frame 或 Spacer(),HStack 会尝试压缩视图来适应内容,导致 Text 被压缩。
LazyHStack 则不同:
它只加载当前可见的视图,并且在滚动时按需加载后续视图,因此不需要为所有内容提前计算布局。
由于 LazyHStack 不会预加载所有视图,它的布局机制更加灵活,避免了 HStack 可能出现的子视图被压缩的问题。
但需要注意的是,LazyHStack可能会让显示的视图变大,因此应该使用frame限制宽度和高度。
总结
在 HStack 中,Image 和 Text 没有设置明确的 frame 时,HStack 会尝试压缩视图以适应内容,导致:
1、Image 可能无法正常显示。
2、Text 被压缩到极小尺寸甚至隐藏。
核心原因为
1、HStack 立即加载所有子视图,并根据其内容大小布局。
2、如果没有明确 frame 或 Spacer(),HStack 会尝试缩小视图导致 Text 被挤压。
3、LazyHStack 由于按需加载视图,不会提前压缩内容,因此不会出现这个问题。
最后的解决方案为,Text使用 fixedSize() 防止被压缩。或者使用LazyHStack解决大数据量视图时的性能问题,同时防止Text被压缩。