Критерии
Простой тестовый пример может определить цвет фона для представления ViewController и загрузить изображение и маску.Затем UITapGestureRecognizer добавляется в представление ViewController и в UIImageView.
При применении цвета фона к представлению ViewController легко увидеть, работает ли маскирование.
Если вы затем коснетесь непрозрачной области, касание должно быть получено UIImageViewв противном случае представление ViewController должно получить сигнал.
Размер изображения и изображения маски
В большинстве случаев размер изображения и изображения маски или, как минимум, соотношение сторон изображенияизображение и изображение маски одинаковы.Имеет смысл использовать тот же contentMode
для маскировки UIImageView, что и для исходного UIImageView, в противном случае при изменении режима содержимого в InterfaceBuilder может произойти смещение не позднее.
Test Case
Поэтому контрольный пример может выглядеть следующим образом:
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, который возвращает при нажатии на прозрачный пиксель, что он не отвечает за него.
Это может бытьдостигается путем переопределения этого метода:
func point(inside point: CGPoint,
with event: UIEvent?) -> Bool
см. документацию здесь: https://developer.apple.com/documentation/uikit/uiview/1622533-point
Возвращает логическое значение, указывающее, содержит ли получатель указанную точку.
На SO уже есть этот крутой ответ, который немного адаптирован: https://stackoverflow.com/a/27923457
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 на CustomImageView
в XCode в инспекторе идентичности.
Если вы теперь нажмете на прозрачные области, представление ViewController в фоновом режиме получает касание.Если вы нажмете на непрозрачные области, наш просмотр изображений получит касание.
Demo
Вот короткая демонстрация приведенного выше кода с использованием изображения и маски извопрос: