Instantly share code, notes, and snippets.
Created
September 19, 2016 12:47
-
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 Raghvendra7/aa83773f4e368cd12dca38eabc2a41d2 to your computer and use it in GitHub Desktop.
FloatingButton( MaterialDesign) for ios
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
import Foundation | |
import UIKit | |
import QuartzCore | |
@IBDesignable | |
public class FloatingButton: UIButton { | |
//PROPERTIES RIPPLE EFFECT - USAGE PROGRAMMATICALY | |
public var ripplePercent: Float = 2.0 { | |
didSet { | |
setupRippleView() | |
} | |
} | |
public var rippleColor: UIColor = UIColor(white: 0.9, alpha: 1) { | |
didSet { | |
rippleView.backgroundColor = rippleColor | |
} | |
} | |
public var rippleBackgroundColor: UIColor = UIColor(white: 0.95, alpha: 1) { | |
didSet { | |
rippleBackgroundView.backgroundColor = rippleBackgroundColor | |
} | |
} | |
private var rippleMask: CAShapeLayer? { | |
get { | |
if !rippleOverBounds { | |
let maskLayer = CAShapeLayer() | |
maskLayer.path = UIBezierPath(roundedRect: bounds, | |
cornerRadius: layer.cornerRadius).CGPath | |
return maskLayer | |
} else { | |
return nil | |
} | |
} | |
} | |
//PROPERTIES RIPPLE EFFECT - USAGE INTERFACE BUILDER | |
@IBInspectable public var rippleOverBounds: Bool = false | |
@IBInspectable public var shadowRippleRadius: Float = 1 | |
@IBInspectable public var shadowRippleEnable: Bool = true | |
@IBInspectable public var trackTouchLocation: Bool = false | |
@IBInspectable public var buttonBackgroundColor: UIColor = UIColor(red:0.96, green:0.26, blue:0.21, alpha:1.0) //Red Color Material Design | |
//FOR DESIGN | |
private let rippleView = UIView() | |
private let rippleBackgroundView = UIView() | |
//FOR DATA | |
private var tempShadowRadius: CGFloat = 0 | |
private var tempShadowOpacity: Float = 0 | |
//MARK: INITIALISERS | |
required public init(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder)! | |
//setup() | |
} | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
setup(frame) | |
} | |
init () { | |
super.init(frame: CGRectZero) | |
setup(frame) | |
} | |
//MARK: LIFE OF VIEW | |
override public func layoutSubviews() { | |
super.layoutSubviews() | |
setupRippleView() | |
let oldCenter = rippleView.center | |
rippleView.center = oldCenter | |
rippleBackgroundView.layer.frame = bounds | |
rippleBackgroundView.layer.mask = rippleMask | |
} | |
//MARK: SETUP SimpleFloatingButton | |
//General setup of the view | |
private func setup(frame:CGRect) { | |
setupViewFrame(frame) | |
setupRippleView() | |
rippleBackgroundView.backgroundColor = rippleBackgroundColor | |
rippleBackgroundView.frame = bounds | |
layer.addSublayer(rippleBackgroundView.layer) | |
layer.shadowOpacity = 0.5 | |
layer.shadowOffset = CGSize(width: 0.0, height: 1.0) | |
rippleBackgroundView.layer.addSublayer(rippleView.layer) | |
rippleBackgroundView.alpha = 0 | |
} | |
//Setup the frame view | |
private func setupViewFrame(frame:CGRect){ | |
//Defaull Value | |
var dim: CGFloat = UIScreen.mainScreen().bounds.height / 8 | |
var y: CGFloat = UIScreen.mainScreen().bounds.height - dim - 20 | |
var x: CGFloat = UIScreen.mainScreen().bounds.width - dim - 20 | |
if(UIDeviceOrientationIsLandscape(UIDevice.currentDevice().orientation)) { | |
dim = UIScreen.mainScreen().bounds.height / 6 | |
y = UIScreen.mainScreen().bounds.height - dim - 20 | |
x = UIScreen.mainScreen().bounds.width - dim - 20 | |
} | |
let newFrame: CGRect = CGRectMake(0, 0, dim, dim) | |
self.frame = newFrame | |
self.frame = frame | |
//CGRectMake(x, y, self.frame.height, self.frame.height) | |
self.layer.cornerRadius = 0.5 * self.frame.height | |
} | |
//Setup the ripple effect | |
private func setupRippleView() { | |
let size: CGFloat = CGRectGetWidth(bounds) * CGFloat(ripplePercent) | |
let x: CGFloat = (CGRectGetWidth(bounds)/2) - (size/2) | |
let y: CGFloat = (CGRectGetHeight(bounds)/2) - (size/2) | |
let corner: CGFloat = size/2 | |
rippleView.backgroundColor = rippleColor | |
rippleView.frame = CGRectMake(x, y, size, size) | |
rippleView.layer.cornerRadius = corner | |
} | |
//Draw the cross on button | |
override public func drawRect(rect: CGRect) { | |
let path = UIBezierPath(ovalInRect: rect) | |
buttonBackgroundColor.setFill() | |
path.fill() | |
//set up the width and height variables | |
//for the horizontal stroke | |
// let plusHeight: CGFloat = 1.0 | |
// let plusWidth: CGFloat = min(bounds.width, bounds.height) * 0.3 | |
// | |
// //create the path | |
// let plusPath = UIBezierPath() | |
// | |
// //set the path's line width to the height of the stroke | |
// plusPath.lineWidth = plusHeight | |
// | |
// //move the initial point of the path | |
// //to the start of the horizontal stroke | |
// plusPath.moveToPoint(CGPoint( | |
// x:bounds.width/2 - plusWidth/2 + 0.5, | |
// y:bounds.height/2 + 0.5)) | |
// | |
// //add a point to the path at the end of the stroke | |
// plusPath.addLineToPoint(CGPoint( | |
// x:bounds.width/2 + plusWidth/2 + 0.5, | |
// y:bounds.height/2 + 0.5)) | |
// | |
// //Vertical Line | |
// //move to the start of the vertical stroke | |
// plusPath.moveToPoint(CGPoint( | |
// x:bounds.width/2 + 0.5, | |
// y:bounds.height/2 - plusWidth/2 + 0.5)) | |
// | |
// //add the end point to the vertical stroke | |
// plusPath.addLineToPoint(CGPoint( | |
// x:bounds.width/2 + 0.5, | |
// y:bounds.height/2 + plusWidth/2 + 0.5)) | |
// | |
// | |
// //set the stroke color | |
// UIColor.whiteColor().setStroke() | |
// | |
// //draw the stroke | |
// plusPath.stroke() | |
} | |
//MARK: Handles Touch Tracking and Animations | |
override public func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool { | |
if trackTouchLocation { | |
rippleView.center = touch.locationInView(self) | |
} | |
UIView.animateWithDuration(0.1, | |
animations: { | |
self.rippleBackgroundView.alpha = 1 | |
}, completion: nil) | |
rippleView.transform = CGAffineTransformMakeScale(0.1, 0.1) | |
UIView.animateWithDuration(0.7, delay: 0, options: .CurveEaseOut, | |
animations: { | |
self.rippleView.transform = CGAffineTransformIdentity | |
}, completion: nil) | |
if shadowRippleEnable { | |
tempShadowRadius = layer.shadowRadius | |
tempShadowOpacity = layer.shadowOpacity | |
let shadowAnim = CABasicAnimation(keyPath:"shadowRadius") | |
shadowAnim.toValue = shadowRippleRadius | |
let opacityAnim = CABasicAnimation(keyPath:"shadowOpacity") | |
opacityAnim.toValue = 1 | |
let groupAnim = CAAnimationGroup() | |
groupAnim.duration = 0.7 | |
groupAnim.fillMode = kCAFillModeForwards | |
groupAnim.removedOnCompletion = false | |
groupAnim.animations = [shadowAnim, opacityAnim] | |
layer.addAnimation(groupAnim, forKey:"shadow") | |
} | |
return super.beginTrackingWithTouch(touch, withEvent: event) | |
} | |
override public func cancelTrackingWithEvent(event: UIEvent?) { | |
super.cancelTrackingWithEvent(event) | |
animateToNormal() | |
} | |
override public func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) { | |
super.endTrackingWithTouch(touch, withEvent: event) | |
animateToNormal() | |
} | |
private func animateToNormal(){ | |
UIView.animateWithDuration(0.1, | |
animations: { | |
self.rippleBackgroundView.alpha = 1 | |
}, | |
completion: {(success: Bool) -> () in | |
UIView.animateWithDuration(0.6 , | |
animations: { | |
self.rippleBackgroundView.alpha = 0 | |
}, completion: nil) | |
} | |
) | |
UIView.animateWithDuration(0.7, delay: 0, | |
options: .CurveEaseOut, | |
animations: { | |
self.rippleView.transform = CGAffineTransformIdentity | |
let shadowAnim = CABasicAnimation(keyPath:"shadowRadius") | |
shadowAnim.toValue = self.tempShadowRadius | |
let opacityAnim = CABasicAnimation(keyPath:"shadowOpacity") | |
opacityAnim.toValue = self.tempShadowOpacity | |
let groupAnim = CAAnimationGroup() | |
groupAnim.duration = 0.7 | |
groupAnim.fillMode = kCAFillModeForwards | |
groupAnim.removedOnCompletion = false | |
groupAnim.animations = [shadowAnim, opacityAnim] | |
self.layer.addAnimation(groupAnim, forKey:"shadowBack") | |
}, completion: nil) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment