Instantly share code, notes, and snippets.
Created
September 1, 2016 10:02
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save zetasq/7a156c3c685b95c76ebb370537f0b9b1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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