您可以创建一个图像视图并将其设置为您的蒙版。请注意,这不适用于动画。如果您想将遮罩设置为不同形状的动画,您应该将遮罩添加到视图的 CALayer 并使用 CALayerAnimation,正如您所提到的。没那么糟糕。
下面我将概述如何生成具有透明部分(孔)的图像,您可以将其用作图像视图中的蒙版。但是,如果您的目标是为孔的大小、形状或位置设置动画,这将不起作用。您必须为每一帧重新生成遮罩图像,这会非常慢。
以下是使用图像视图作为蒙版的静态视图的效果:
使用 UIGraphicsBeginImageContextWithOptions() UIGraphicsImageRenderer 创建一个图像,该图像对您的大部分图像都是不透明的,并且在您想要一个洞的地方有一个透明的“洞”。
然后在您的图像视图中安装该图像,并使该图像视图成为您的掩码。
创建带有透明圆角矩形“孔”的大部分不透明图像的代码可能如下所示:
/**
Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
- Parameter size: The size of the image to create
- Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
- Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
*/
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { (context) in
let frame = CGRect(origin: .zero, size: size)
UIColor.white.setFill()
context.fill(frame)
let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
context.cgContext.setFillColor(UIColor.clear.cgColor)
context.cgContext.setBlendMode(.clear)
roundedRect.fill()
}
return image
}
还有一个viewDidLoad 方法安装一个带有一个带有孔的掩码图像视图的 UIImageView 可能看起来像这样:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .cyan
let size = CGSize(width: 200, height: 300)
let origin = CGPoint(x: 50, y: 50)
let frame = CGRect(origin: origin, size: size)
let imageView = UIImageView(frame: frame)
imageView.image = UIImage(named: "TestImage")
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
imageView.layer.borderWidth = 2
//Create a mask image view the same size as the (image) view we will be masking
let maskView = UIImageView(frame: imageView.bounds)
//Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)
//Install the image with the "hole" into the mask image view
maskView.image = maskImage
//Make the maskView the ImageView's mask
imageView.mask = maskView /// set the mask
}
}
我使用上面的代码创建了一个示例项目。你可以在这里从 Github 下载它:
https://github.com/DuncanMC/UIImageMask.git
我刚刚更新了项目,还展示了如何使用 CAShapeLayer 作为图像视图层上的蒙版来做同样的事情。这样做,可以对遮罩层的路径进行动画更改。
新版本有一个分段控件,可让您选择是使用视图的 mask 属性中的 UIImage 来屏蔽图像视图,还是通过用作图像视图层上的蒙版的 CAShapeLayer。
对于 CAShapeLayer 版本,遮罩层的路径是整个图像视图大小的矩形,其中绘制了第二个较小的圆角矩形。然后将形状图层上的缠绕规则设置为“偶数/奇数”规则,这意味着如果您必须跨越偶数个形状边界才能到达某个点,则该点被视为在形状之外。这使您能够创建我们需要的空心形状。
当您选择图层蒙版选项时,它会启用一个动画按钮,该按钮会随机更改蒙版中的“剪切”透明矩形。
创建掩码路径的函数如下所示:
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
let fullRect = UIBezierPath(rect: maskLayer.frame)
let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
fullRect.append(roundedRect)
return fullRect
}
做动画的函数是这样的:
@IBAction func handleAnimateButton(_ sender: Any) {
//Create a CABasicAnimation that will change the path of our maskLayer
//Use the keypath "path". That tells the animation object what property we are animating
let animation = CABasicAnimation(keyPath: "path")
animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done
//Use ease-in, ease-out timing, which looks smooth
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.
let transparentRect: CGRect
//Randomly either animate the transparent rect to a different shape or shift it
if Bool.random() {
//Make the transparent rect taller and skinnier
transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
} else {
//Shift the transparent rect to by a random amount that still says inside the image view's bounds.
transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
}
let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
//install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath
//add the animation to the maskLayer. Since the animation's `keyPath` is "path",
//it will animate the layer's "path" property to the "toValue"
maskLayer.add(animation, forKey: nil)
//Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}
结果(使用我自己的示例图像)如下所示:
基于 CALayer 的蒙版动画示例如下所示: