What I'm doing: I am creating a music app. Within this music app, I have a music player that links with Apple Music and Spotify. The music player displays the current song's album art on a UIImageView. Whenever the song is playing, the UIImageView rotates (like a record on a record player).
What my problem is: The album art for the UIImageView is square by default. I am rounding the UIImageView's layer's corners and setting UIImageView.clipsToBounds equal to true to make it appear as a circle. Whenever the UIImageView rotates, some of the pixels outside of the UIImageView's layer (the part that is cut off after rounding the image) are bleeding through.
Here is what the bug looks like: https://www.youtube.com/watch?v=OJxX5PQc7Jo&feature=youtu.be
My code: The UIImageView is rounded by setting its layer's cornerRadius equal to UIImageView.frame.height / 2 and setting 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]
*/
}
}
Whenever a button is pressed, the UIImageView begins to rotate. I've used the following extension to make UIView's rotate:
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)
}
}
}
I also have the following extension to end the rotation:
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
}
}
}
This is how these extension functions are used in the context of my view controller:
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)
}
}
}
So, all together, MyViewController looks something like this:
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)
}
}
}
from Not all pixels clipped when using UIView.layer.clipsToBounds = true in conjunction with CABasicAnimation
No comments:
Post a Comment