Как обрезать UIImage для маски в 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
}

введите здесь описание изображения введите здесь описание изображения


person Cristian    schedule 20.11.2018    source источник
comment
stackoverflow.com/questions/32041420/ Я думаю, что этот вопрос аналогичен   -  person Ruban4Axis    schedule 20.11.2018


Ответы (1)


Критерии

Простой тестовый пример может определить цвет фона для представления 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
comment
Спасибо, это точно ответил на мой вопрос - person Cristian; 02.12.2018