project_files/HedgewarsMobile/Classes/MNEValueTrackingSlider.m
author koda
Fri, 17 Feb 2012 18:23:36 +0100
changeset 6700 e04da46ee43c
parent 6679 d8b98aa486a6
child 6908 896ed2afcfb8
permissions -rwxr-xr-x
the most important commit of the year

//
// MNEValueTrackingSlider
//
// Copyright 2012 Michael Neuwert
// "You can use the code in your own project and modify it as you like."
// http://blog.neuwert-media.com/2012/04/customized-uislider-with-visual-value-tracking/
//


#import "MNEValueTrackingSlider.h"

#pragma mark -
#pragma mark Private UIView subclass rendering the popup showing slider value
@interface SliderValuePopupView : UIView
@property (nonatomic, retain) UIFont *font;
@property (nonatomic, copy) NSString *text;
@property (nonatomic) float arrowOffset;
@end

@implementation SliderValuePopupView

@synthesize font = _font;
@synthesize text = _text;
@synthesize arrowOffset = _arrowOffset;

-(id) initWithFrame:(CGRect) frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.font = [UIFont boldSystemFontOfSize:18];
    }
    return self;
}

-(void) dealloc {
    self.text = nil;
    self.font = nil;
    [super dealloc];
}

-(void) drawRect:(CGRect) rect {
    // Create the path for the rounded rectangle
    CGRect roundedRect = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, self.bounds.size.width, floorf(self.bounds.size.height * 0.8));
    UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:roundedRect cornerRadius:6.0];
    roundedRectPath.lineWidth = 2.0f;

    // Create the arrow path
    UIBezierPath *arrowPath = [UIBezierPath bezierPath];
    /*
    // Make sure the arrow offset is nice
    if (-self.arrowOffset + 1 > CGRectGetMidX(self.bounds) / 2)
        self.arrowOffset = -CGRectGetMidX(self.bounds) / 2 + 1;
    if (self.arrowOffset > CGRectGetMidX(self.bounds) / 2)
        self.arrowOffset = CGRectGetMidX(self.bounds) / 2 -1;
     */

    CGFloat midX = CGRectGetMidX(self.bounds) + self.arrowOffset;
    CGPoint p0 = CGPointMake(midX, CGRectGetMaxY(self.bounds));
    [arrowPath moveToPoint:p0];
    [arrowPath addLineToPoint:CGPointMake((midX - 10.0), CGRectGetMaxY(roundedRect))];
    [arrowPath addLineToPoint:CGPointMake((midX + 10.0), CGRectGetMaxY(roundedRect))];
    [arrowPath closePath];

    // Attach the arrow path to the rounded rect
    [roundedRectPath appendPath:arrowPath];

    // Color various sections
    [[UIColor blackColor] setFill];
    [roundedRectPath fill];
    [[UIColor whiteColor] setStroke];
    [roundedRectPath stroke];
    [[UIColor whiteColor] setFill];
    [arrowPath fill];

    // Draw the text
    if (self.text) {
        [[UIColor lightYellowColor] set];
        CGSize s = [_text sizeWithFont:self.font];
        CGFloat yOffset = (roundedRect.size.height - s.height) / 2;
        CGRect textRect = CGRectMake(roundedRect.origin.x, yOffset, roundedRect.size.width, s.height);

        [_text drawInRect:textRect
                 withFont:self.font
            lineBreakMode:UILineBreakModeWordWrap
                alignment:UITextAlignmentCenter];
    }
}

@end

#pragma mark -
#pragma mark MNEValueTrackingSlider implementations
@implementation MNEValueTrackingSlider

@synthesize thumbRect, textValue;

#pragma Private methods

-(void) _constructSlider {
    valuePopupView = [[SliderValuePopupView alloc] initWithFrame:CGRectZero];
    valuePopupView.backgroundColor = [UIColor clearColor];
    valuePopupView.alpha = 0.0;
    [self addSubview:valuePopupView];
}

-(void) _fadePopupViewInAndOut:(BOOL)aFadeIn {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.25];
    if (aFadeIn) {
        valuePopupView.alpha = 1.0;
    } else {
        valuePopupView.alpha = 0.0;
    }
    [UIView commitAnimations];
}

-(void) _positionAndUpdatePopupView {
    CGRect _thumbRect = self.thumbRect;
    CGRect popupRect = CGRectOffset(_thumbRect, 0, -floorf(_thumbRect.size.height * 1.5));
    // (-100, -15) determines the size of the the rect
    popupRect = CGRectInset(popupRect, -100, -15);

    // this prevents drawing the popup outside the slider view
    if (popupRect.origin.x < -self.frame.origin.x+5)
        popupRect.origin.x = -self.frame.origin.x+5;
    else if (popupRect.origin.x > self.superview.frame.size.width - popupRect.size.width - self.frame.origin.x - 5)
        popupRect.origin.x = self.superview.frame.size.width - popupRect.size.width - self.frame.origin.x - 5;
    //else if (CGRectGetMaxX(popupRect) > CGRectGetMaxX(self.superview.bounds))
    //    popupRect.origin.x = CGRectGetMaxX(self.superview.bounds) - CGRectGetWidth(popupRect) - 1.0;

    valuePopupView.arrowOffset = CGRectGetMidX(_thumbRect) - CGRectGetMidX(popupRect);

    valuePopupView.frame = popupRect;
    valuePopupView.text = self.textValue;
    [valuePopupView setNeedsDisplay];
}

#pragma mark Memory management

-(id) initWithFrame:(CGRect) frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self _constructSlider];
    }
    return self;
}

-(id) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self _constructSlider];
    }
    return self;
}

-(void) dealloc {
    [valuePopupView release];
    [textValue release];
    [super dealloc];
}

#pragma mark -
#pragma mark UIControl touch event tracking
-(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    // Fade in and update the popup view
    CGPoint touchPoint = [touch locationInView:self];
    // Check if the knob is touched. Only in this case show the popup-view
    if(CGRectContainsPoint(CGRectInset(self.thumbRect, -14.0, -12.0), touchPoint)) {
        [self _positionAndUpdatePopupView];
        [self _fadePopupViewInAndOut:YES];
    }
    return [super beginTrackingWithTouch:touch withEvent:event];
}

-(BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    // Update the popup view as slider knob is being moved
    [self _positionAndUpdatePopupView];
    return [super continueTrackingWithTouch:touch withEvent:event];
}

-(void) cancelTrackingWithEvent:(UIEvent *)event {
    [super cancelTrackingWithEvent:event];
}

-(void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    // Fade out the popoup view
    [self _fadePopupViewInAndOut:NO];
    [super endTrackingWithTouch:touch withEvent:event];
}

#pragma mark -
#pragma mark Custom property accessors
-(CGRect) thumbRect {
    CGRect trackRect = [self trackRectForBounds:self.bounds];
    CGRect thumbR = [self thumbRectForBounds:self.bounds
                                         trackRect:trackRect
                                             value:self.value];
    return thumbR;
}

@end