考虑以下简化的代码片段
import SwiftUI
struct ContentView: View {
var body: some View {
ScrollView {
// Some other views here that should also be covered
VStack {
ForEach(0..<8) { i in
ListElement()
.padding(.horizontal)
}
}
}
}
}
struct ListElement: View {
@State private var isFocused: Bool = false
var body: some View {
Button(action: {
self.isFocused.toggle()
}, label: {
Text(isFocused ? "I am focused" : "I am not focused")
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(.gray)
.background(
isFocused ?
Color.pink
.opacity(0.5)
.frame(width: 10000, height: 10000)
.ignoresSafeArea()
.onTapGesture {
self.isFocused = false
}
: nil
)
})
}
}
#Preview {
ContentView()
}
其结果如下
通常ScrollView包含更多的ListElements。如果用户点击一个ListElement,我希望它调整大小并显示附加信息,这是一个简单的按钮。但是,我也希望除了ListElement之外的所有内容都有一个透明的覆盖层,以便更加强调所选的ListElement,而不强调其他内容。如果用户点击除了所选ListElement之外的任何内容(在这种情况下,是其背景),则相应的ListElement不应再被聚焦。有几个解决方案(如“滚动”类视图),但据我所知,由于依赖于GeometryReader,没有一个在ScrollViews中工作。
在示例代码中,我尝试在每个ListElement上使用一个大得离谱的background
-修饰符来实现这一点,它将覆盖整个屏幕。它看起来像这样:
万岁!VStack在视觉上不再被强调,所选的ListElement清楚地处于焦点位置。但是,如果选择了除最后一个ListElement之外的任何ListElement,则会发生以下情况:
正如您所看到的,background
-修饰符并没有覆盖任何在所选元素之后的ListElement。我假设这是因为SwiftUI中的视图层次结构。有没有什么方法可以将当前选中的ListElement推到层次结构的顶部,这样我就可以覆盖它后面的任何内容?
1条答案
按热度按时间kmynzznz1#
要回答你的问题,你必须将
.zIndex(isFocused ? 1 : -1)
修饰符添加到ListElement的Button中。但是你也必须将焦点状态存储在一个全局位置,因为-我猜-如果一个列表元素是焦点,我们应该从之前焦点的元素(如果有的话)中删除焦点。因此,代码看起来像这样:
虽然它的工作,我并不完全满意这个解决方案。我觉得它很笨拙,我没有100%的信心,它将在每一个场景中工作。
所以我认为一个更好的解决方案是在我们的全部内容上方画一个“封面”,并在特定位置剪一个洞,使屏幕的一部分可见。为了实现这一点,我们必须确定一个给定的元素在屏幕上的确切位置,然后我们必须绘制我们的封面,并使用“reversemask”在该位置切一个洞。这个解决方案有很多优点,比如它不仅可以处理列表元素,还可以处理屏幕上的任何类型的视图。我们也可以将其扩展到“切割”不是矩形,而是任何形状的孔。
此解决方案还有一个未解决的问题:如果你有很多元素,比如说你滚动你的内容,那么孔的位置将是不正确的。如果检查scrollview的contentOffset和subscrollview的offset.x与测量的CGRect的origin. x,就可以解决这个问题。我没有实现它,因为我的代码使用它会太复杂,但是您可以使用this作为帮助轻松地扩展此代码。