#import <UIKit/UIKit.h>
@interface SevenSwitch : UIControl
@property (nonatomic, assign) BOOL on;
@property (nonatomic, strong) UIColor *inactiveColor;
@property (nonatomic, strong) UIColor *activeColor;
@property (nonatomic, strong) UIColor *onColor;
@property (nonatomic, strong) UIColor *borderColor;
@property (nonatomic, strong) UIColor *knobColor;
@property (nonatomic, strong) UIColor *shadowColor;
@property (nonatomic, assign) BOOL isRounded;
@property (nonatomic, strong) UIImage *onImage;
@property (nonatomic, strong) UIImage *offImage;
- (void)setOn:(BOOL)on animated:(BOOL)animated;
- (BOOL)isOn;
@end
#import "SevenSwitch.h"
#import <QuartzCore/QuartzCore.h>
@interface SevenSwitch () {
UIView *background
UIView *knob
UIImageView *onImageView
UIImageView *offImageView
double startTime
BOOL isAnimating
}
@property (nonatomic, strong) UIView *containerView
- (void)showOn:(BOOL)animated
- (void)showOff:(BOOL)animated
- (void)setup
@end
@implementation SevenSwitch
@synthesize inactiveColor, activeColor, onColor, borderColor, knobColor, shadowColor
@synthesize onImage, offImage
@synthesize isRounded
@synthesize on
#pragma mark init Methods
- (id)init {
self = [super initWithFrame:CGRectMake(0, 0, 50, 30)]
if (self) {
[self setup]
}
return self
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder]
if (self) {
[self setup]
}
return self
}
- (id)initWithFrame:(CGRect)frame
{
// use the default values if CGRectZero frame is set
CGRect initialFrame
if (CGRectIsEmpty(frame)) {
initialFrame = CGRectMake(0, 0, 50, 30)
}
else {
initialFrame = frame
}
self = [super initWithFrame:initialFrame]
if (self) {
[self setup]
}
return self
}
- (void)setup {
// default values
self.on = NO
self.isRounded = YES
self.inactiveColor = [UIColor clearColor]
self.activeColor = [UIColor colorWithRed:0.89f green:0.89f blue:0.89f alpha:1.00f]
self.onColor = [UIColor colorWithRed:0.30f green:0.85f blue:0.39f alpha:1.00f]
self.borderColor = [UIColor colorWithRed:0.89f green:0.89f blue:0.91f alpha:1.00f]
self.knobColor = [UIColor whiteColor]
self.shadowColor = [UIColor grayColor]
// background
background = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]
background.backgroundColor = [UIColor clearColor]
background.layer.cornerRadius = self.frame.size.height * 0.5
background.layer.borderColor = self.borderColor.CGColor
background.layer.borderWidth = 1.0
background.userInteractionEnabled = NO
[self addSubview:background]
// images
onImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width - self.frame.size.height, self.frame.size.height)]
// [onImageView setContentMode:UIViewContentModeScaleToFill]
onImageView.alpha = 0
onImageView.contentMode = UIViewContentModeCenter
[self addSubview:onImageView]
offImageView = [[UIImageView alloc] initWithFrame:CGRectMake(self.frame.size.height, 0, self.frame.size.width - self.frame.size.height, self.frame.size.height)]
offImageView.alpha = 1.0
offImageView.contentMode = UIViewContentModeCenter
[self addSubview:offImageView]
// knob
knob = [[UIView alloc] initWithFrame:CGRectMake(1, 1, self.frame.size.height - 2, self.frame.size.height - 2)]
knob.backgroundColor = self.knobColor
knob.layer.cornerRadius = (self.frame.size.height * 0.5) - 1
knob.layer.shadowColor = self.shadowColor.CGColor
knob.layer.shadowRadius = 2.0
knob.layer.shadowOpacity = 0.5
knob.layer.shadowOffset = CGSizeMake(0, 3)
knob.layer.masksToBounds = NO
knob.userInteractionEnabled = NO
[self addSubview:knob]
isAnimating = NO
}
#pragma mark Touch Tracking
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super beginTrackingWithTouch:touch withEvent:event]
// start timer to detect tap later in endTrackingWithTouch:withEvent:
startTime = [[NSDate date] timeIntervalSince1970]
// make the knob larger and animate to the correct color
CGFloat activeKnobWidth = self.bounds.size.height - 2 + 5
isAnimating = YES
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
if (self.on) {
knob.frame = CGRectMake(self.bounds.size.width - (activeKnobWidth + 1), knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
background.backgroundColor = self.onColor
}
else {
knob.frame = CGRectMake(knob.frame.origin.x, knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
background.backgroundColor = self.activeColor
}
} completion:^(BOOL finished) {
isAnimating = NO
}]
return YES
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super continueTrackingWithTouch:touch withEvent:event]
// Get touch location
CGPoint lastPoint = [touch locationInView:self]
// update the switch to the correct visuals depending on if
// they moved their touch to the right or left side of the switch
if (lastPoint.x > self.bounds.size.width * 0.5)
[self showOn:YES]
else
[self showOff:YES]
return YES
}
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
[super endTrackingWithTouch:touch withEvent:event]
// capture time to see if this was a tap action
double endTime = [[NSDate date] timeIntervalSince1970]
double difference = endTime - startTime
BOOL previousValue = self.on
// determine if the user tapped the switch or has held it for longer
if (difference <= 0.2) {
CGFloat normalKnobWidth = self.bounds.size.height - 2
knob.frame = CGRectMake(knob.frame.origin.x, knob.frame.origin.y, normalKnobWidth, knob.frame.size.height)
[self setOn:!self.on animated:YES]
}
else {
// Get touch location
CGPoint lastPoint = [touch locationInView:self]
// update the switch to the correct value depending on if
// their touch finished on the right or left side of the switch
if (lastPoint.x > self.bounds.size.width * 0.5)
[self setOn:YES animated:YES]
else
[self setOn:NO animated:YES]
}
if (previousValue != self.on)
[self sendActionsForControlEvents:UIControlEventValueChanged]
}
- (void)cancelTrackingWithEvent:(UIEvent *)event {
[super cancelTrackingWithEvent:event]
// just animate back to the original value
if (self.on)
[self showOn:YES]
else
[self showOff:YES]
}
- (void)layoutSubviews {
[super layoutSubviews]
if (!isAnimating) {
CGRect frame = self.frame
// background
background.frame = CGRectMake(0, 0, frame.size.width, frame.size.height)
background.layer.cornerRadius = self.isRounded ? frame.size.height * 0.5 : 2
// images
onImageView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height)
[onImageView setContentMode:UIViewContentModeScaleAspectFit]
offImageView.frame = CGRectMake(0, 0, frame.size.width, frame.size.height)
[offImageView setContentMode:UIViewContentModeScaleAspectFit]
// knob
CGFloat normalKnobWidth = frame.size.height - 2
if (self.on)
knob.frame = CGRectMake(frame.size.width - (normalKnobWidth + 1), 1, frame.size.height - 2, normalKnobWidth)
else
knob.frame = CGRectMake(1, 1, normalKnobWidth, normalKnobWidth)
knob.layer.cornerRadius = self.isRounded ? (frame.size.height * 0.5) - 1 : 2
}
}
#pragma mark Setters
- (void)setInactiveColor:(UIColor *)color {
inactiveColor = color
if (!self.on && !self.isTracking)
background.backgroundColor = color
}
- (void)setOnColor:(UIColor *)color {
onColor = color
if (self.on && !self.isTracking) {
background.backgroundColor = color
background.layer.borderColor = color.CGColor
}
}
- (void)setBorderColor:(UIColor *)color {
borderColor = color
if (!self.on)
background.layer.borderColor = color.CGColor
}
- (void)setKnobColor:(UIColor *)color {
knobColor = color
knob.backgroundColor = color
}
- (void)setShadowColor:(UIColor *)color {
shadowColor = color
knob.layer.shadowColor = color.CGColor
}
- (void)setOnImage:(UIImage *)image {
onImage = image
onImageView.image = image
}
- (void)setOffImage:(UIImage *)image {
offImage = image
offImageView.image = image
}
- (void)setIsRounded:(BOOL)rounded {
isRounded = rounded
if (rounded) {
background.layer.cornerRadius = self.frame.size.height * 0.5
knob.layer.cornerRadius = (self.frame.size.height * 0.5) - 1
}
else {
background.layer.cornerRadius = 2
knob.layer.cornerRadius = 2
}
}
- (void)setOn:(BOOL)isOn {
[self setOn:isOn animated:NO]
}
- (void)setOn:(BOOL)isOn animated:(BOOL)animated {
on = isOn
if (isOn) {
[self showOn:animated]
}
else {
[self showOff:animated]
}
}
#pragma mark Getters
- (BOOL)isOn {
return self.on
}
#pragma mark State Changes
- (void)showOn:(BOOL)animated {
CGFloat normalKnobWidth = self.bounds.size.height - 2
CGFloat activeKnobWidth = normalKnobWidth + 5
if (animated) {
isAnimating = YES
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
if (self.tracking)
knob.frame = CGRectMake(self.bounds.size.width - (activeKnobWidth + 1), knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
else
knob.frame = CGRectMake(self.bounds.size.width - (normalKnobWidth + 1), knob.frame.origin.y, normalKnobWidth, knob.frame.size.height)
background.backgroundColor = self.onColor
background.layer.borderColor = self.onColor.CGColor
onImageView.alpha = 1.0
offImageView.alpha = 0
} completion:^(BOOL finished) {
isAnimating = NO
}]
}
else {
if (self.tracking)
knob.frame = CGRectMake(self.bounds.size.width - (activeKnobWidth + 1), knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
else
knob.frame = CGRectMake(self.bounds.size.width - (normalKnobWidth + 1), knob.frame.origin.y, normalKnobWidth, knob.frame.size.height)
background.backgroundColor = self.onColor
background.layer.borderColor = self.onColor.CGColor
onImageView.alpha = 1.0
offImageView.alpha = 0
}
}
- (void)showOff:(BOOL)animated {
CGFloat normalKnobWidth = self.bounds.size.height - 2
CGFloat activeKnobWidth = normalKnobWidth + 5
if (animated) {
isAnimating = YES
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut|UIViewAnimationOptionBeginFromCurrentState animations:^{
if (self.tracking) {
knob.frame = CGRectMake(1, knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
background.backgroundColor = self.activeColor
}
else {
knob.frame = CGRectMake(1, knob.frame.origin.y, normalKnobWidth, knob.frame.size.height)
background.backgroundColor = self.inactiveColor
}
background.layer.borderColor = self.borderColor.CGColor
onImageView.alpha = 0
offImageView.alpha = 1.0
} completion:^(BOOL finished) {
isAnimating = NO
}]
}
else {
if (self.tracking) {
knob.frame = CGRectMake(1, knob.frame.origin.y, activeKnobWidth, knob.frame.size.height)
background.backgroundColor = self.activeColor
}
else {
knob.frame = CGRectMake(1, knob.frame.origin.y, normalKnobWidth, knob.frame.size.height)
background.backgroundColor = self.inactiveColor
}
background.layer.borderColor = self.borderColor.CGColor
onImageView.alpha = 0
offImageView.alpha = 1.0
}
}
@end