Circular Rotating Gradient Ring-Swift & XCode
By: Charlie Arcodia
Tags: iOS, Swift Programming, Mobile App Development, iOS App Development

Prerequisites

A basic understanding of Swift and iOS Development.

Jumping in

A unique simplistic style can make your application stand out and add a nice feel. Using gradients correctly could be exactly what’s needed to spice up the UI! Let’s get started.

Add Some Views

We need a UIView and a UIImageView. Add these two blocks. Name them anything you would like. I chose profilePhotoRingView and profilePhoto. Make sure you use your own image name though. Being explicit with your naming conventions will help out big time as the application scales and more devs join the team!

lazy var profilePhotoRingView : UIView = { let oli = UIView() oli.translatesAutoresizingMaskIntoConstraints = false oli.backgroundColor = .clear oli.isUserInteractionEnabled = true oli.clipsToBounds = true oli.layer.masksToBounds = true return oli }() lazy var profilePhoto : UIImageView = { let oli = UIImageView() let image = UIImage(named : "demo_wall")?.withRenderingMode(.alwaysOriginal) oli.image = image oli.translatesAutoresizingMaskIntoConstraints = false oli.backgroundColor = UIColor .white oli.contentMode = .scaleAspectFill oli.layer.masksToBounds = true oli.layer.borderColor = UIColor .white.cgColor oli.layer.borderWidth = 1.5 oli.isUserInteractionEnabled = true return oli }()

Add and Constrain

Add the profilePhotoRingView first so it appears under the profilePhoto. Call the addViews function in the viewDidLoad.

func addViews() { self.view.addSubview(profilePhotoRingView) self.view.addSubview(profilePhoto)

self.profilePhotoRingView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0).isActive = true self.profilePhotoRingView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0).isActive = true self.profilePhotoRingView.heightAnchor.constraint(equalToConstant: 50).isActive = true self.profilePhotoRingView.widthAnchor.constraint(equalToConstant: 50).isActive = true self.profilePhotoRingView.layer.cornerRadius = 25 self.profilePhoto.centerYAnchor.constraint(equalTo: self.profilePhotoRingView.centerYAnchor, constant: 0).isActive = true self.profilePhoto.centerXAnchor.constraint(equalTo: self.profilePhotoRingView.centerXAnchor, constant: 0).isActive = true self.profilePhoto.heightAnchor.constraint(equalToConstant: 48).isActive = true self.profilePhoto.widthAnchor.constraint(equalToConstant: 48).isActive = true self.profilePhoto.layer.cornerRadius = 24 }

Gradient Setup

Add the gradientLayer variable and a Boolean to control viewDidLayoutSubviews().

var gradientLayer: CAGradientLayer! var hasViewBeenLaidOut : Bool = false

Add the handleGradientLayer function.

func handleGradientLayer() { self.gradientLayer = CAGradientLayer() self.gradientLayer.frame = profilePhotoRingView.bounds self.gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) self.gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) self.gradientLayer.colors = [UIColor .blue.withAlphaComponent(1.0).cgColor, UIColor .blue.withAlphaComponent(0.9).cgColor, UIColor .blue.withAlphaComponent(0.8).cgColor, UIColor .blue.withAlphaComponent(0.7).cgColor, UIColor .orange.withAlphaComponent(0.6).cgColor, UIColor .orange.withAlphaComponent(0.5).cgColor, UIColor .orange.withAlphaComponent(0.4).cgColor, UIColor .orange.withAlphaComponent(0.3).cgColor, UIColor .orange.withAlphaComponent(0.2).cgColor, UIColor .orange.withAlphaComponent(0.1).cgColor] self.profilePhotoRingView.layer.addSublayer(self.gradientLayer) self.addGradientRingRotation() }

Why so many colors?! This allows for the decending fading effect on top of the gradient, very cool! One error here though, let’s add the addGradientRingRotation function. Firstly, add the CAAnimationDelegate protocol.

func addGradientRingRotation() { let fullRotation = CABasicAnimation(keyPath: "transform.rotation") fullRotation.delegate = self fullRotation.fromValue = NSNumber(floatLiteral: 0) fullRotation.toValue = NSNumber(floatLiteral: Double(CGFloat.pi * 2)) fullRotation.duration = 3.0 fullRotation.repeatCount = Float.infinity fullRotation.isRemovedOnCompletion = false profilePhotoRingView.layer.add(fullRotation, forKey: "360") }

Easy right! Now, isRemovedOnCompletion will make sure your gradient keeps spinning if you exit the application and return — This is just one possible implementation.

Lastly, let’s add the handleGradientLayer function to viewDidLayoutSubviews().

override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if self.hasViewBeenLaidOut == false { self.handleGradientLayer() self.hasViewBeenLaidOut = true } }

That’s it! Now watch your gradient spin in all her glory! Feel free to play with the duration and alpha values of the gradient to get the exact look you’re after! If you would like to see more gradient implementations, let me know in the comments below!