mask在SwiftUI中是用于遮罩控件的,它可以根据我们提供的形状或者图片来裁剪控件的可见区域。比如,我们可以用圆形的mask来让一个图片控件变成圆形,或者用三角形mask一个按钮,只展示按钮的一个角等等
下面先看一个简单的例子,利用mask来显示图片。
Image("0")
.resizable()
.scaledToFit()
.foregroundColor(.green)
我们使用爱心来显示美女图片,使用 mask很容易到达这个需求。
使用mask显示图形
方法定义如下,需要放回一个View
.mask(<#T##mask: () -> View##() -> View#>)
我们只需要在mask的返回体中加入如下代码:
.mask {
Image(systemName: "heart.fill")
.resizable()
.scaledToFit()
}
mask指定的形状是什么,最终的图形就是什么。所以最终的图形形状都有mask设定的图形决定
使用 mask 创建渐变评分控件
在很多场景中,都可以见到评分控件。它是由5个五角星组成,初始状态为灰色,当我们点击五角星就会点亮颜色变成黄色。
首先,来创建五角星。
HStack {
ForEach(1..<6) { index in
Image(systemName: "star.fill")
.font(.largeTitle)
.foregroundColor(.orange)
}
}
使用上述代码即可创建五个五角星,但是它不具有动态功能。我们来加上点击功能。
首先,我们需要创建一个变量,来保存当前点击的是第几个星星。
@State var rating: Int = 1
其次,我们需要使用不同前景色来区分已点击和未点击的五角星
.foregroundColor(rating >= index ? .orange : .gray)
最后,我们给五角星加入点击事件。并且在点击事件中把当前的index值赋给rating。
.onTapGesture {
rating = index
}
下面是效果
但是,如何给控件加渐变色呢?我们就要使用mask来完成这个任务。
首先,把创建五角星的代码提取成为一个变量starView。保持主body简单整洁,这个很重要。
var starView: some View {
HStack {
ForEach(1..<6) { index in
Image(systemName: "star.fill")
.font(.largeTitle)
.foregroundColor(rating >= index ? .orange : .gray)
.onTapGesture {
rating = index
}
}
}
}
其次,我们给StartView添加一个overlay,将mask添加到overlay上。
ZStack {
starView
.overlay(
Rectangle()
.foregroundColor(.yellow)
)
}
我们可以看到,因为overlay的rectangle不知道宽度是多少,所以它会默认等于主视图的宽度。
我们需要使用GometryReader来读取大小,从而控制overlay的宽度来展示底部五角星视图。因为overlay是在五角星视图上面的。所以我们只有控制好上层视图的宽度,才可以看到下层视图。
我们还是把Overlay的内容提取成一个变量
var overlayView: some View {
GeometryReader { geo in
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
.allowsHitTesting(false)
}
frame(width: CGFloat(rating) / 5 * geo.size.width) 用于计算overlay的视图宽度,根据点击的index来决定视图显示的宽度是多少
当我们点击五角星时,会发现视图的宽度和我们预料的一样。但是还需要最终的一个mask。
在前面的例子中,我们明白。mask作为一个用于遮罩控件。当你给定mask什么形状,最终的物体就会是给定的mask的样子。所以我们把五角星的形状再设定给mask,就可以显示五角星了。
ZStack {
starView
.overlay(overlayView.mask(starView))
}
var overlayView: some View {
GeometryReader { geo in
Rectangle()
.foregroundColor(.yellow)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
}
此时,就可以显示正常的五角星。
但是,此时你会发现。可以把灰色五角星点亮,但是却无法再变成灰色。
那是因为,我们把灰色变成黄色时,触发的是真实的五角星的点击事件。但是当我们把五个五角星全部点亮,此时就显示的是OverlayView的视图,OverlayView的视图阻止了底部的点击事件。所以导致无法响应事件,从而无法改变值,也就无法改变颜色了。那么我们需要把OverlayView的点击事件禁用掉。
使用以下方法禁用视图点击事件
.allowsHitTesting(false)
allowsHitTesting
在 SwiftUI 中,allowsHitTesting属性主要用于控制视图是否可以接收点击(hit test)事件。
- 如果allowsHitTesting设置为true,则该视图可以接收点击事件,默认为true。
- 如果设置为false,则该视图将不再响应任何点击事件,点击会透传给下层的视图。
需要注意的是它只影响点击事件,其他触摸事件如长按、拖拽等不受影响。
好了,设置属性后,变得正常了。
加个动画
在点击事件中加入动画,让事件感觉动起来
.onTapGesture {
withAnimation(.easeInOut) {
rating = index
}
}
加入渐变
有了以上基础,渐变就很简单了。我们只需要把overlayView的foregroundColor去掉。换成fill,然后在fill中使用一个渐变来填充就OK了
var overlayView: some View {
GeometryReader { geo in
Rectangle()
// .foregroundColor(.yellow)
.fill(
LinearGradient(colors: [Color.blue, Color.green], startPoint: .leading, endPoint: .trailing)
)
.frame(width: CGFloat(rating) / 5 * geo.size.width)
}
.allowsHitTesting(false)
}
我是用来清爽的颜色来做为渐变色,以上是最终效果。
大家有什么看法呢?欢迎留言讨论。
公众号:RobotPBQ