Критерии
Простой тестовый пример может определить цвет фона для представления ViewController и загрузить изображение и маску. Затем UITapGestureRecognizer добавляется в представление ViewController и в UIImageView.
При применении цвета фона к представлению ViewController легко увидеть, работает ли маскирование.
Если вы затем коснетесь непрозрачной области, касание должно быть получено UIImageView, в противном случае представление ViewController должно получить касание.
Размер изображения и маски
В большинстве случаев размер изображения и изображения маски или, по крайней мере, соотношение сторон изображения и изображения маски совпадают. Имеет смысл использовать тот же contentMode
для маскирования UIImageView, что и для исходного 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, который возвращает при нажатии на прозрачный пиксель, что он за него не отвечает.
Это может быть достигнуто путем переопределения этого метода:
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 в фоновом режиме получит касание. Если вы коснетесь непрозрачных областей, наше представление изображения получит касание.
Демо
Вот короткая демонстрация приведенного выше кода с использованием изображения и маски из вопроса:
а>
person
Stephan Schlecht
schedule
02.12.2018