SwiftUI中的.clipped()
修改器能够将视图的内容修剪到视图的bounds内,任何超出视图bounds的内容都会被裁剪。但是.clipped()
并不会影响hit testing,被修剪的View依然能够接收到超出视图可见区域的tap和click事件
例子
- 我们有一个300x300的正方形,我们把它限制在100x100的frame内,并且在100x100的上面添加了边框以便我们能够看到它
- SwiftUI的view默认不会修剪内容,所以我们依然能够看到300x300的正方形
- 图里面蓝色的边框就是我们限制的100x100的frame边框
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
// Set view to 100×100 → renders out of bounds
.frame(width: 100, height: 100)
.border(.blue)
例子修改1
现在我们给100x100的frame添加.clipped
修改器,再添加一个tap手势。
再在正方形上方添加一个button,来演示button没有被响应的情况
VStack {
Button("You can't tap me!") {
buttonTapCount += 1
}
.buttonStyle(.borderedProminent)
Rectangle()
.fill(.orange.gradient)
.frame(width: 300, height: 300)
.frame(width: 100, height: 100)
.clipped()
.onTapGesture {
rectTapCount += 1
}
}
当我们运行代码的时候就会发现点击button一点响应也没有。 这是因为我们将300x300的正方形限制在了100x100的frame里,虽然超出的部分不可见,但由于它是在button之后布局且它的控件布局遮盖了button,会先相应hit test,事件在这就断开了。所以根本点击不到button上
使用.contentShape 修复问题
.contentShape
修改器定义了一个View可以被hit test的区域。
我们给100x100frame的view添加 .contentShape(Rectangle())
将hit test限制在该区域内,这样button就能继续被响应了
注意 .contentShape(Rectangle())和.clipped()
不能交换,他们是有顺序的。
SwiftUI中每应用一次 视图修改器,都会创建一个新的View,如果交换的话相当于把hit test限制在了300x300的frame内,并不能解决问题。我们需要将它限制在100x100的frame内
总结
- clipped视图修改器不会影响视图的hit test区域,同样的,clipShape也不影响hit test区域
- 通过
.contentShape(Rectangle())
和这些修改器组合来保证hit test逻辑和UI的一致是一个不错的做法