Skip to content

Instantly share code, notes, and snippets.

@vinczebalazs
Last active January 29, 2020 12:27
Show Gist options
  • Save vinczebalazs/1e8ac8f4891901cef85d8e648c82081a to your computer and use it in GitHub Desktop.
Save vinczebalazs/1e8ac8f4891901cef85d8e648c82081a to your computer and use it in GitHub Desktop.
A UITextField subclass with the ability to show a loading spinner in its right view.
import UIKit
class LoadingTextField: UITextField {
// MARK: Public Properties
var spinnerSpeed = 0.75
var spinnerWidth: CGFloat = 3
var spinnerDiameter: CGFloat = 18
var isSpinnerRounded = true
var offset: CGFloat = 16
lazy var spinnerColor = textColor?.withAlphaComponent(0.6)
var isLoading: Bool { _isLoading }
// MARK: Private Properties
private var _isLoading = false
private let containerView = UIView()
// MARK: Methods
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
// Draw a semi circle.
containerView.frame = CGRect(x: 0, y: 0, width: spinnerDiameter, height: spinnerDiameter)
let spinnerLayer = CAShapeLayer()
let circlePath = UIBezierPath(arcCenter: CGPoint(x: containerView.frame.width / 2,
y: containerView.frame.width / 2),
radius: containerView.frame.width / 2,
startAngle: .zero, endAngle: .pi * 2, clockwise: true)
spinnerLayer.path = circlePath.cgPath
spinnerLayer.strokeColor = spinnerColor?.cgColor
spinnerLayer.fillColor = nil
spinnerLayer.lineWidth = spinnerWidth
spinnerLayer.strokeStart = 0.5
spinnerLayer.strokeEnd = 1
spinnerLayer.lineCap = isSpinnerRounded ? .round : .butt
containerView.layer.addSublayer(spinnerLayer)
// Add it to the right view of the textfield.
rightView = containerView
rightViewMode = .always
// Rotate the semi circle.
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.fromValue = 0.0
rotateAnimation.toValue = CGFloat(.pi * 2.0)
rotateAnimation.duration = spinnerSpeed
rotateAnimation.repeatCount = .greatestFiniteMagnitude
containerView.layer.add(rotateAnimation, forKey: nil)
}
func startLoading() {
if isLoading { return }
_isLoading = true
// Fade the loading spinner.
containerView.alpha = 0
UIView.animate(withDuration: 0.25, animations: {
self.containerView.alpha = 1
})
}
func stopLoading() {
if !isLoading { return }
_isLoading = false
// Fade the loading spinner.
UIView.animate(withDuration: 0.25, animations: {
self.containerView.alpha = 0
}, completion: { _ in
self.containerView.removeFromSuperview()
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment