Swift ios вырезал скругленный прямоугольник, позволяя менять цвет - PullRequest
0 голосов
/ 02 июля 2019

Я использую этот подход, чтобы вырезать закругленное прямоугольное «окно» из фона:

override func draw(_ rect: CGRect) {
        guard let rectsArray = rectsArray else {
            return
        }

        for holeRect in rectsArray {
            let holeRectIntersection = rect.intersection(holeRect)

            if let context = UIGraphicsGetCurrentContext() {
                let roundedWindow = UIBezierPath(roundedRect: holeRect, cornerRadius: 15.0)
                if holeRectIntersection.intersects(rect) {
                    context.addPath(roundedWindow.cgPath)
                    context.clip()
                    context.clear(holeRectIntersection)
                    context.setFillColor(UIColor.clear.cgColor)
                    context.fill(holeRectIntersection)
                }
            }

        }
    }

В layoutSubviews() Я обновляю цвет фона и добавляю мой прямоугольник «оконная рамка»:

override func layoutSubviews() {
        super.layoutSubviews()
        backgroundColor = self.baseMoodColour
        isOpaque = false
        self.rectsArray?.removeAll()
        self.rectsArray = [dragAreaView.frame]
}

Я добавляю прямоугольник сюда, потому что layoutSubviews() обновляет размер "оконной рамы" (т. Е. Прямоугольник изменяется после layoutSubviews() запуска).

Основной механизмработает как положено, однако, если я изменю цвет фона, окно выреза заполняется черным.Поэтому мне интересно, как я могу анимировать изменение цвета фона при такой настройке?То есть я хочу анимировать цвет области за пределами окна выреза (окно остается чистым).

Я пытался обновить backgroundColor напрямую, а также с помощью didSet в аксессоре пользовательской переменной цвета в моем подклассе UIView, но оба вызывают одно и то же заполнение "окна".

    var baseMoodColour: UIColor {
        didSet {
            self.backgroundColor = baseMoodColour
            self.setNeedsDisplay()
        }
    }

Ответы [ 3 ]

0 голосов
/ 02 июля 2019

Проблема в краткосрочной перспективе заключается в том, что это просто то, что делает clear, если цвет фона непрозрачен. Просто придайте фоновому цвету некоторую прозрачность - даже крошечную прозрачность, настолько крошечную, чтобы человеческий глаз не мог ее воспринять - и теперь clear прорежет дыру в изображении.

Например, ваш код работает нормально, если вы установите цвет фона вида UIColor.green.withAlphaComponent(0.99).

Кстати, вы должны удалить строки о UIColor.clear; это красная сельдь Вы также должны обрезать линии около backgroundColor; Вы не должны перекрашивать цвет фона в свой контекст. Это две разные вещи.


Проблема в долгосрочной перспективе заключается в том, что вы не пытаетесь сделать дыру в представлении. Вы должны использовать маску вместо. Это единственный способ получить анимацию, сохраняя дыру.

0 голосов
/ 09 июля 2019

Отвечая на мой собственный вопрос, основываясь на предложении @ matt (и связанном примере), я сделал это с помощью CAShapeLayer. В моих требованиях была дополнительная «заминка», так как у меня есть пара представлений поверх той, которую мне нужно было замаскировать. Итак, я сделал маскировку так:

func cutOutWindow() {
        // maskedBackgroundView is an additional view, inserted ONLY for the mask
        let r = self.maskedBackgroundView.bounds
        // Adjust frame for dragAreaView's border
        var dragSize = self.dragAreaView.frame.size
        var dragPosition = self.dragAreaView.frame.origin
        dragSize.width -= 6.0
        dragSize.height -= 6.0
        dragPosition.x += 3.0
        dragPosition.y += 3.0
        let r2 = CGRect(x: dragPosition.x, y: dragPosition.y, width: dragSize.width, height: dragSize.height)
        let roundedWindow = UIBezierPath(roundedRect: r2, cornerRadius: 15.0)
        let mask = CAShapeLayer()
        let path = CGMutablePath()
        path.addPath(roundedWindow.cgPath)
        path.addRect(r)
        mask.path = path
        mask.fillRule = kCAFillRuleEvenOdd
        self.maskedBackgroundView.layer.mask = mask
    }

Затем я должен был применить изменение цвета к maskedBackgroundView.layer.backgroundColor (т.е. к слою, а не к виду). Имея это на месте, я получаю нужный мне вырез с анимируемыми изменениями цвета. Спасибо @matt за то, что указал мне правильное направление.

0 голосов
/ 02 июля 2019

Попробуйте использовать UIView.animate, вы можете проверить его здесь

UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveEaseOut], animations: {
  self.backgroundColor = someNewColour
  //Generally 
  //myView.backgroundColor = someNewColor
}, nil)
...