Skip to content

Instantly share code, notes, and snippets.

@zetasq
Created September 1, 2016 10:02
Show Gist options
  • Save zetasq/7a156c3c685b95c76ebb370537f0b9b1 to your computer and use it in GitHub Desktop.
Save zetasq/7a156c3c685b95c76ebb370537f0b9b1 to your computer and use it in GitHub Desktop.
//
// LoadingButton.swift
// Teambition
//
// Created by Zhu Shengqi on 8/11/16.
// Copyright © 2016 Teambition. All rights reserved.
//
import UIKit
import SnapKit
class LoadingButton: UIControl {
//MARK: - Private
private var isLoading = false
private var normalTitleColor = UIColor(red: 0, green: 122.0 / 255, blue: 1, alpha: 1)
private var highlightedTitleColor = UIColor(red: 0, green: 122.0 / 255, blue: 1, alpha: 1).colorWithAlphaComponent(0.5)
private var normalBackgroundColor: UIColor?
private var highlightedBackgroundColor: UIColor?
private var disabledBackgroundColor: UIColor?
lazy var titleLabel: UILabel! = {
let titleLabel = UILabel()
titleLabel.textAlignment = .Center
return titleLabel
}()
private var loadingView: LoadingView?
var tapHandler: (() -> Void)?
func setTitleColor(titleColor: UIColor, forState state: UIControlState) {
if state == .Normal {
normalTitleColor = titleColor
if self.state == .Normal {
titleLabel.textColor = normalTitleColor
}
} else if state == .Highlighted {
highlightedTitleColor = titleColor
if self.state.contains(.Highlighted) {
titleLabel.textColor = highlightedTitleColor
}
}
}
func setBackgroundColor(color: UIColor, forState state: UIControlState) {
if state == .Normal {
normalBackgroundColor = color
if self.state == .Normal {
backgroundColor = normalBackgroundColor
}
} else if state == .Highlighted {
highlightedBackgroundColor = color
if self.state.contains(.Highlighted) {
backgroundColor = highlightedBackgroundColor
}
} else if state == .Disabled {
disabledBackgroundColor = color
if self.state.contains(.Disabled) {
backgroundColor = disabledBackgroundColor
}
}
}
//MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
private func setup() {
titleLabel.textColor = normalTitleColor
addSubview(titleLabel)
titleLabel.snp_makeConstraints { make in
make.edges.equalTo(self)
}
addTarget(self, action: #selector(self.stateChanged), forControlEvents: .AllEvents)
addTarget(self, action: #selector(self.buttonTapped), forControlEvents: .TouchUpInside)
}
override var enabled: Bool {
didSet {
if enabled != oldValue && !isLoading {
updateUI()
}
}
}
func stateChanged() {
if !isLoading {
updateUI()
}
}
func buttonTapped() {
if !isLoading {
highlighted = false
selected = false
updateUI()
tapHandler?()
}
}
private func updateUI() {
if state == .Normal {
titleLabel.textColor = normalTitleColor
backgroundColor = normalBackgroundColor ?? UIColor.primaryColor
} else if state.contains(.Highlighted) {
titleLabel.textColor = highlightedTitleColor
backgroundColor = highlightedBackgroundColor ?? normalBackgroundColor ?? UIColor.primaryColor
} else if state.contains(.Disabled) {
titleLabel.textColor = normalTitleColor.colorWithAlphaComponent(0.5)
backgroundColor = disabledBackgroundColor ?? normalBackgroundColor ?? UIColor.primaryColor
}
}
func startAnimating() {
guard !isLoading else {
return
}
isLoading = true
titleLabel.hidden = true
loadingView = createLoadingView()
addSubview(loadingView!)
loadingView!.snp_makeConstraints { (make) in
make.edges.equalTo(self)
}
loadingView!.startAnimating()
}
func stopAnimating() {
guard isLoading else {
return
}
loadingView?.stopAnimating {
self.loadingView?.removeFromSuperview()
self.loadingView = nil
self.titleLabel.hidden = false
self.updateUI()
self.isLoading = false
}
}
private func createLoadingView() -> LoadingView {
return LoadingView(frame: .zero, animationShapeSize: CGSize(width: 10, height: 10))
}
}
// MARK: - LoadingView
class LoadingView: UIView {
//MARK: - Private
private var viewsArray: [UIView] = []
private let kLoadingViewAlpha: CGFloat = 0.6
private let kScaleFactor: CGFloat = 1.1
private var animationDuration: NSTimeInterval = 1.3
private var animationShapeSize = CGSize(width: 10, height: 10)
//MARK: - Init
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupUI()
}
init(frame: CGRect, animationShapeSize: CGSize) {
super.init(frame: frame)
self.animationShapeSize = animationShapeSize
setupUI()
}
private func setupUI() {
let centerViewFrame = CGRect(x: frame.midX - animationShapeSize.width / 2,
y: frame.midY - animationShapeSize.height / 2,
width: animationShapeSize.width,
height: animationShapeSize.height)
let centerView = createCircleView(with: centerViewFrame)
addSubview(centerView)
centerView.snp_makeConstraints { make in
make.width.equalTo(animationShapeSize.width)
make.height.equalTo(animationShapeSize.height)
make.centerX.equalTo(self)
make.centerY.equalTo(self)
}
var leftViewFrame = centerViewFrame
leftViewFrame.origin.x = centerViewFrame.origin.x - animationShapeSize.width - 5
let leftView = createCircleView(with: leftViewFrame)
addSubview(leftView)
leftView.snp_makeConstraints { make in
make.width.equalTo(animationShapeSize.width)
make.height.equalTo(animationShapeSize.height)
make.right.equalTo(centerView.snp_left).offset(-5)
make.centerY.equalTo(centerView)
}
var rightViewFrame = centerViewFrame
rightViewFrame.origin.x = centerViewFrame.origin.x + animationShapeSize.width + 5
let rightView = createCircleView(with: rightViewFrame)
addSubview(rightView)
rightView.snp_makeConstraints { make in
make.width.equalTo(animationShapeSize.width)
make.height.equalTo(animationShapeSize.height)
make.left.equalTo(centerView.snp_right).offset(5)
make.centerY.equalTo(centerView)
}
viewsArray = [leftView, centerView, rightView]
}
private func createCircleView(with circleFrame: CGRect) -> UIView {
let shapeLayer = CAShapeLayer()
shapeLayer.path = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: circleFrame.width, height: circleFrame.height)).CGPath
let ovalView = UIView(frame: circleFrame)
ovalView.backgroundColor = UIColor.whiteColor()
ovalView.layer.mask = shapeLayer
ovalView.alpha = kLoadingViewAlpha
return ovalView
}
func startAnimating() {
let leftView = viewsArray[0]
let centerView = viewsArray[1]
let rightView = viewsArray[2]
UIView.animateKeyframesWithDuration(animationDuration, delay:0, options:[.BeginFromCurrentState, .Repeat], animations: {
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 0.3, animations: {
centerView.alpha = self.kLoadingViewAlpha
rightView.alpha = self.kLoadingViewAlpha
leftView.alpha = 1
leftView.transform = CGAffineTransformScale(CGAffineTransformIdentity, self.kScaleFactor, self.kScaleFactor)
rightView.transform = CGAffineTransformIdentity
centerView.transform = CGAffineTransformIdentity
})
UIView.addKeyframeWithRelativeStartTime(0.3, relativeDuration: 0.3, animations: {
leftView.transform = CGAffineTransformIdentity
rightView.transform = CGAffineTransformIdentity
centerView.transform = CGAffineTransformScale(CGAffineTransformIdentity, self.kScaleFactor, self.kScaleFactor)
leftView.alpha = self.kLoadingViewAlpha
rightView.alpha = self.kLoadingViewAlpha
centerView.alpha = 1
})
UIView.addKeyframeWithRelativeStartTime(0.6, relativeDuration: 0.3, animations: {
rightView.transform = CGAffineTransformScale(CGAffineTransformIdentity, self.kScaleFactor, self.kScaleFactor)
leftView.transform = CGAffineTransformIdentity
centerView.transform = CGAffineTransformIdentity
leftView.alpha = self.kLoadingViewAlpha
centerView.alpha = self.kLoadingViewAlpha
rightView.alpha = 1
})
UIView.addKeyframeWithRelativeStartTime(0.9, relativeDuration: 0.1, animations: {
leftView.alpha = self.kLoadingViewAlpha
centerView.alpha = self.kLoadingViewAlpha
rightView.alpha = self.kLoadingViewAlpha
rightView.transform = CGAffineTransformIdentity
leftView.transform = CGAffineTransformIdentity
centerView.transform = CGAffineTransformIdentity
})
}, completion: nil)
}
func stopAnimating(completion: () -> Void) {
let leftView = viewsArray[0]
let centerView = viewsArray[1]
let rightView = viewsArray[2]
UIView.animateWithDuration(0, animations: {
leftView.alpha = self.kLoadingViewAlpha
centerView.alpha = self.kLoadingViewAlpha
rightView.alpha = self.kLoadingViewAlpha
rightView.transform = CGAffineTransformIdentity
leftView.transform = CGAffineTransformIdentity
centerView.transform = CGAffineTransformIdentity
}, completion: { _ in
completion()
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment