
This video is only available to subscribers. Start a subscription today to get access to this and 469 other videos.
Designing a Custom Download Button - Part 1
Episode
#242
|
14 minutes
| published on
October 27, 2016
| Uses Xcode-8.2, swift-3.0
Subscribers Only
In this episode we create a custom control to serve as our download button. We start by creating a circular progress indicator using CAShapeLayer, then move on to subclassing UIControl to provide our image view and touch handling.
This episode is part of a series: Large File Downloads.
Episode Links
Creating a Circular Progress Indicator
class CircularProgressLayer : CAShapeLayer {
var progress: CGFloat = 0 {
didSet {
update()
}
}
var fillLayer: CAShapeLayer!
var color: UIColor = .lightGray {
didSet {
strokeColor = color.cgColor
fillLayer.fillColor = color.cgColor
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
_setupLayer()
}
init(frame: CGRect) {
super.init()
self.frame = frame
_setupLayer()
}
private func _setupLayer() {
isOpaque = false
lineWidth = 2
strokeColor = color.cgColor
fillColor = UIColor.clear.cgColor
fillLayer = CAShapeLayer()
fillLayer.fillColor = color.cgColor
addSublayer(fillLayer)
update()
}
func update() {
path = UIBezierPath(ovalIn: bounds).cgPath
let fillPath = UIBezierPath()
let radius = frame.size.height/2
let center = CGPoint(x: frame.size.width/2, y: frame.size.height/2)
fillPath.move(to: center)
let startAngle: CGFloat = -.pi/2
let endAngle: CGFloat = (2 * .pi ) * progress + startAngle
fillPath.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
fillLayer.path = fillPath.cgPath
}
}
Testing out in the storyboard:
let cpl = CircularProgressLayer(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
let page = PlaygroundPage.current
let view = UIView(frame: cpl.bounds.insetBy(dx: -10, dy: -10))
view.backgroundColor = UIColor.white
view.layer.addSublayer(cpl)
func tick() {
guard cpl.progress <= 1.0 else { return }
cpl.progress += 0.018
DispatchQueue.main.async {
Thread.sleep(forTimeInterval: 0.01)
tick()
}
}
page.liveView = view
tick()
Creating a custom Button control
We're not subclassing UIButton
because we want to have total control over the UI that is drawn on screen.
class DownloadButton : UIControl {
lazy var downloadImage = UIImage(named: "Download")
lazy var completedImage = UIImage(named: "Checkmark")
var imageView: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
_setupButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
_setupButton()
}
private func _setupButton() {
imageView = UIImageView(frame: bounds)
imageView.contentMode = .scaleAspectFit
imageView.image = downloadImage
addSubview(imageView)
addTarget(self, action: #selector(DownloadButton.addHighlight), for: [.touchDown, .touchDragEnter])
addTarget(self, action: #selector(DownloadButton.removeHighlight), for: [.touchUpInside, .touchDragExit])
}
func addHighlight() {
backgroundColor = .red
}
func removeHighlight() {
backgroundColor = .clear
}
}