Не все пиксели обрезаются при использовании UIView.layer.clipsToBounds = true в сочетании с CABasicAnimation - PullRequest
0 голосов
/ 18 октября 2019

Что я делаю: Я создаю музыкальное приложение. В этом музыкальном приложении у меня есть музыкальный проигрыватель, который связан с Apple Music и Spotify. Музыкальный проигрыватель отображает обложку альбома текущей песни на UIImageView. Всякий раз, когда воспроизводится песня, UIImageView вращается (как запись на проигрывателе).

В чем моя проблема: Обложка альбома для UIImageView по умолчанию квадратная,Я округляю углы UIImageView layer и задаю UIImageView.clipsToBounds равным true, чтобы он выглядел как круг. Всякий раз, когда UIImageView вращается, некоторые пиксели за пределами UIImageView layer (части, которая обрезается после округления изображения) просвечивают.

Вот чтоошибка выглядит так: https://www.youtube.com/watch?v=OJxX5PQc7Jo&feature=youtu.be

Мой код: UIImageView округляется, устанавливая layer s cornerRadius равным UIImageView.frame.height / 2 и устанавливаяUIImageView.clipsToBounds = true:

class MyViewController: UIViewController {

    @IBOutlet var albumArtImageView: UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()
        albumArtImageView.layer.cornerRadius = albumArtImageView.frame.height / 2
        albumArtImageView.clipsToBounds = true

        //I've also tried the following code, and am getting the same behavior:
        /*
        albumArtImageView.layer.cornerRadius = albumArtImageView.frame.height / 2
        albumArtImageView.layer.masksToBounds = true
        albumArtImageView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner,.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
        */
    }
}

Каждый раз, когда нажимается кнопка, UIImageView начинает вращаться. Я использовал следующее extension, чтобы сделать вращение UIView:

extension UIView {
    func rotate(duration: Double = 1, startPoint: CGFloat) {
        if layer.animation(forKey: UIView.kRotationAnimationKey) == nil {
            let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")

            rotationAnimation.fromValue = startPoint
            rotationAnimation.toValue = (CGFloat.pi * 2.0) + startPoint
            rotationAnimation.duration = duration
            rotationAnimation.repeatCount = Float.infinity

            layer.add(rotationAnimation, forKey: UIView.kRotationAnimationKey)
        }
    }
}

У меня также есть следующее расширение, чтобы завершить вращение:

extension UIView {

    ...

    func stopRotating(beginTime: Double!, startingAngle: CGFloat) -> CGFloat? {
        if layer.animation(forKey: UIView.kRotationAnimationKey) != nil {
            let animation = layer.animation(forKey: UIView.kRotationAnimationKey)!
            let elapsedTime = CACurrentMediaTime() - beginTime
            let angle = elapsedTime.truncatingRemainder(dividingBy: animation.duration)/animation.duration

            layer.transform = CATransform3DMakeRotation((CGFloat(angle) * (2 * CGFloat.pi)) + startingAngle, 0.0, 0.0, 1.0)
            layer.removeAnimation(forKey: UIView.kRotationAnimationKey)
            return (CGFloat(angle) * (2 * CGFloat.pi)) + startingAngle
        } else {
            return nil
        }
    }
}

Вот какэти функции расширения используются в контексте моего контроллера представления:

class MyViewController: UIViewController {

    var songBeginTime: Double!
    var currentRecordAngle: CGFloat = 0.0
    var isPlaying = false

    ...

    @IBAction func playButtonPressed(_ sender: Any) {
        if isPlaying {
            if let angle = albumArtImageView.stopRotating(beginTime: songBeginTime, startingAngle: currentRecordAngle) {
                currentRecordAngle = angle
            }
            songBeginTime = nil
        } else {
            songBeginTime = CACurrentMediaTime()
            albumArtImageView.rotate(duration: 3, startPoint: currentRecordAngle)
        }
    }
}

Итак, все вместе MyViewController выглядит примерно так:

class MyViewController: UIViewController {

    @IBOutlet var albumArtImageView: UIImageView()

    var songBeginTime: Double!
    var currentRecordAngle: CGFloat = 0.0
    var isPlaying = false

    override func viewDidLoad() {
        super.viewDidLoad()
        albumArtImageView.layer.cornerRadius = albumArtImageView.frame.height / 2
        albumArtImageView.clipsToBounds = true
    }

    @IBAction func playButtonPressed(_ sender: Any) {
        if isPlaying {
            if let angle = albumArtImageView.stopRotating(beginTime: songBeginTime, startingAngle: currentRecordAngle) {
                currentRecordAngle = angle
            }
            songBeginTime = nil
        } else {
            songBeginTime = CACurrentMediaTime()
            albumArtImageView.rotate(duration: 3, startPoint: currentRecordAngle)
        }
    }
}

Ответы [ 2 ]

1 голос
/ 21 октября 2019

Я копирую ваш код в проект и могу воспроизвести эту проблему. Но если вы добавите анимацию в другой CALayer, это, похоже, решит проблему.

extension UIView {

static var kRotationAnimationKey: String {
    return "kRotationAnimationKey"
}

func makeAnimationLayer() -> CALayer {

    let results: [CALayer]? = layer.sublayers?.filter({ $0.name ?? "" == "animationLayer" })

    let animLayer: CALayer
    if let sublayers = results, sublayers.count > 0 {
        animLayer = sublayers[0]
    }
    else {
        animLayer = CAShapeLayer()
        animLayer.name = "animationLayer"
        animLayer.frame = self.bounds
        animLayer.contents = UIImage(named: "imageNam")?.cgImage
        layer.addSublayer(animLayer)
    }

    return animLayer
}

func rotate(duration: Double = 1, startPoint: CGFloat) {
    if layer.animation(forKey: UIView.kRotationAnimationKey) == nil {

        let animLayer = makeAnimationLayer()

        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
        rotationAnimation.fromValue = startPoint
        rotationAnimation.toValue = (CGFloat.pi * 2.0) + startPoint
        rotationAnimation.duration = duration
        rotationAnimation.repeatCount = Float.infinity
        animLayer.add(rotationAnimation, forKey: UIView.kRotationAnimationKey)
    }
}

func stopRotating(beginTime: Double!, startingAngle: CGFloat) -> CGFloat? {

    let animLayer = makeAnimationLayer()

    if animLayer.animation(forKey: UIView.kRotationAnimationKey) != nil {
        let animation = animLayer.animation(forKey: UIView.kRotationAnimationKey)!
        let elapsedTime = CACurrentMediaTime() - beginTime
        let angle = elapsedTime.truncatingRemainder(dividingBy: animation.duration)/animation.duration
        animLayer.transform = CATransform3DMakeRotation(CGFloat(angle) * (2 * CGFloat.pi) + startingAngle, 0.0, 0.0, 1.0)
        animLayer.removeAnimation(forKey: UIView.kRotationAnimationKey)
        return (CGFloat(angle) * (2 * CGFloat.pi)) + startingAngle
    }
    else {
        return nil
    }
}
1 голос
/ 18 октября 2019

Зажим к границам не включает закругленные углы. Попробуйте вместо этого использовать маскировку углов.

class MyViewController: UIViewController {

    @IBOutlet var albumArtImageView: UIImageView()

    var songBeginTime: Double!
    var currentRecordAngle: CGFloat = 0.0
    var isPlaying = false

    override func viewDidLoad() {
        super.viewDidLoad()
        albumArtImageView.layer.cornerRadius = albumArtImageView.frame.height / 2
        albumArtImageView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner,.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
    }

    @IBAction func playButtonPressed(_ sender: Any) {
        if isPlaying {
            if let angle = albumArtImageView.stopRotating(beginTime: songBeginTime, startingAngle: currentRecordAngle) {
                currentRecordAngle = angle
            }
            songBeginTime = nil
        } else {
            songBeginTime = CACurrentMediaTime()
            albumArtImageView.rotate(duration: 3, startPoint: currentRecordAngle)
        }
    }
}
...