如何在 Swift 中裁剪 UIImage 以遮罩
How to crop UIImage to mask in Swift
我有一个 UIImage,我使用另一个 UIImage 屏蔽了它。唯一的问题是被屏蔽的 UIImage 之外的区域仍然是用户可交互的。如何将 UIImage 完全裁剪为另一个图像而不是蒙版。
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let imageMask = UIImageView()
imageMask.image = //image to mask
imageMask.frame = photoImageView.bounds
imageView.mask = imageMask
}
条件
一个简单的测试用例可以为 ViewController 的视图定义背景颜色并加载图像和遮罩。然后将 UITapGestureRecognizer 添加到 ViewController 视图和 UIImageView。
将背景颜色应用到 ViewController 视图时,很容易看出遮罩是否有效。
如果您随后点击非透明区域,UIImageView 应接收点击,否则 ViewController 视图应接收点击。
图像和蒙版图像大小
在大多数情况下,图像和蒙版图像的大小或至少图像和蒙版图像的纵横比相同。
使用与原始 UIImageView 相同的 contentMode
来屏蔽 UIImageView 是有意义的,否则最晚在 InterfaceBuilder 中更改内容模式时会出现错位。
测试用例
因此测试用例可能如下所示:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
private let maskView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.image = UIImage(named: "testimage")
self.maskView.image = UIImage(named: "mask")
self.imageView.mask = maskView
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped))
self.view.addGestureRecognizer(tapGestureRecognizer)
let imageViewGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(iamgeViewTapped))
self.imageView.addGestureRecognizer(imageViewGestureRecognizer)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.maskView.contentMode = self.imageView.contentMode
self.maskView.frame = self.imageView.bounds
}
@objc private func backgroundTapped() {
print ("background tapped!")
}
@objc private func iamgeViewTapped() {
print ("image view tapped!")
}
}
此代码已经 运行。然而,正如预期的那样,点击 UIImageView 的透明区域也会到达此处。
CustomImageView
因此我们需要一个 CustomImageView,它 returns 当点击一个它不负责的透明像素时。
这可以通过覆盖此方法来实现:
func point(inside point: CGPoint,
with event: UIEvent?) -> Bool
在此处查看文档:https://developer.apple.com/documentation/uikit/uiview/1622533-point
Returns a Boolean value indicating whether the receiver contains the specified point.
SO 上已经有这个很酷的答案,只是稍作调整:
import UIKit
class CustomImageView: UIImageView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return self.alphaFromPoint(point: point) > 32
}
private func alphaFromPoint(point: CGPoint) -> UInt8 {
var pixel: [UInt8] = [0, 0, 0, 0]
let colorSpace = CGColorSpaceCreateDeviceRGB();
let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
if let context = CGContext(data: &pixel,
width: 1,
height: 1,
bitsPerComponent: 8,
bytesPerRow: 4,
space: colorSpace,
bitmapInfo: alphaInfo.rawValue) {
context.translateBy(x: -point.x, y: -point.y)
self.layer.render(in: context)
}
return pixel[3]
}
}
不要忘记在身份检查器中将 ImageView 的自定义 class 更改为 Xcode 中的 CustomImageView
。
如果您现在点击透明区域,背景中 ViewController 的视图将被点击。如果您点击非透明区域,我们的图像视图会收到点击。
演示
下面是使用问题中的图像和遮罩的上述代码的简短演示:
我有一个 UIImage,我使用另一个 UIImage 屏蔽了它。唯一的问题是被屏蔽的 UIImage 之外的区域仍然是用户可交互的。如何将 UIImage 完全裁剪为另一个图像而不是蒙版。
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let imageMask = UIImageView()
imageMask.image = //image to mask
imageMask.frame = photoImageView.bounds
imageView.mask = imageMask
}
条件
一个简单的测试用例可以为 ViewController 的视图定义背景颜色并加载图像和遮罩。然后将 UITapGestureRecognizer 添加到 ViewController 视图和 UIImageView。
将背景颜色应用到 ViewController 视图时,很容易看出遮罩是否有效。
如果您随后点击非透明区域,UIImageView 应接收点击,否则 ViewController 视图应接收点击。
图像和蒙版图像大小
在大多数情况下,图像和蒙版图像的大小或至少图像和蒙版图像的纵横比相同。
使用与原始 UIImageView 相同的 contentMode
来屏蔽 UIImageView 是有意义的,否则最晚在 InterfaceBuilder 中更改内容模式时会出现错位。
测试用例
因此测试用例可能如下所示:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
private let maskView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
self.imageView.image = UIImage(named: "testimage")
self.maskView.image = UIImage(named: "mask")
self.imageView.mask = maskView
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped))
self.view.addGestureRecognizer(tapGestureRecognizer)
let imageViewGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(iamgeViewTapped))
self.imageView.addGestureRecognizer(imageViewGestureRecognizer)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.maskView.contentMode = self.imageView.contentMode
self.maskView.frame = self.imageView.bounds
}
@objc private func backgroundTapped() {
print ("background tapped!")
}
@objc private func iamgeViewTapped() {
print ("image view tapped!")
}
}
此代码已经 运行。然而,正如预期的那样,点击 UIImageView 的透明区域也会到达此处。
CustomImageView
因此我们需要一个 CustomImageView,它 returns 当点击一个它不负责的透明像素时。
这可以通过覆盖此方法来实现:
func point(inside point: CGPoint,
with event: UIEvent?) -> Bool
在此处查看文档:https://developer.apple.com/documentation/uikit/uiview/1622533-point
Returns a Boolean value indicating whether the receiver contains the specified point.
SO 上已经有这个很酷的答案,只是稍作调整:
import UIKit
class CustomImageView: UIImageView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return self.alphaFromPoint(point: point) > 32
}
private func alphaFromPoint(point: CGPoint) -> UInt8 {
var pixel: [UInt8] = [0, 0, 0, 0]
let colorSpace = CGColorSpaceCreateDeviceRGB();
let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
if let context = CGContext(data: &pixel,
width: 1,
height: 1,
bitsPerComponent: 8,
bytesPerRow: 4,
space: colorSpace,
bitmapInfo: alphaInfo.rawValue) {
context.translateBy(x: -point.x, y: -point.y)
self.layer.render(in: context)
}
return pixel[3]
}
}
不要忘记在身份检查器中将 ImageView 的自定义 class 更改为 Xcode 中的 CustomImageView
。
如果您现在点击透明区域,背景中 ViewController 的视图将被点击。如果您点击非透明区域,我们的图像视图会收到点击。
演示
下面是使用问题中的图像和遮罩的上述代码的简短演示: