Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone Multi touch interactive to CorePlot

I'm developing an iPhone application with core-plot chart. With the help of some tutorials, i managed to do it with single touch and drag. How can I make it with multiple touch and drag? Anybody please help me?

enter image description here

ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    EskPlotTheme *defaultTheme = [[EskPlotTheme alloc] init];
    linePlot = [[EskLinePlot alloc] init];
    linePlot.delegate = self;
    [linePlot renderInLayer:lineHostingView withTheme:defaultTheme]; 
    [defaultTheme release];    
}

EskLinePlot.m

- (id)init
{
    self = [super init];
    if (self) 
    {
        // setting up the sample data here.
        sampleData = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:6000],
                                                      [NSNumber numberWithInt:3000],
                                                      [NSNumber numberWithInt:2000],
                                                      [NSNumber numberWithInt:5000],
                                                      [NSNumber numberWithInt:7000],
                                                      [NSNumber numberWithInt:8500],
                                                      [NSNumber numberWithInt:6500], nil];

    }
    return self;
}

- (void)renderInLayer:(CPTGraphHostingView *)layerHostingView withTheme:(CPTTheme *)theme
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    CGRect bounds = layerHostingView.bounds;

    // Create the graph and assign the hosting view.
    graph = [[CPTXYGraph alloc] initWithFrame:bounds];
    layerHostingView.hostedGraph = graph;
    [graph applyTheme:theme];

    graph.plotAreaFrame.masksToBorder = NO;

    // chang the chart layer orders so the axis line is on top of the bar in the chart.
    NSArray *chartLayers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:CPTGraphLayerTypePlots],
                                                            [NSNumber numberWithInt:CPTGraphLayerTypeMajorGridLines], 
                                                            [NSNumber numberWithInt:CPTGraphLayerTypeMinorGridLines],  
                                                            [NSNumber numberWithInt:CPTGraphLayerTypeAxisLines], 
                                                            [NSNumber numberWithInt:CPTGraphLayerTypeAxisLabels], 
                                                            [NSNumber numberWithInt:CPTGraphLayerTypeAxisTitles], 
                                                            nil];
    graph.topDownLayerOrder = chartLayers;    
    [chartLayers release];

    // Add plot space for horizontal charts
    graph.paddingLeft = 60.0;
    graph.paddingTop = 70.0;
    graph.paddingRight = 20.0;
    graph.paddingBottom = 20.0;

    // Setup plot space
    CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)graph.defaultPlotSpace;
    plotSpace.allowsUserInteraction = YES;
    plotSpace.delegate = self;
    int sampleCount = [sampleData count]-1;
    plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0f) length:CPTDecimalFromFloat(sampleCount)];
    plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0f) length:CPTDecimalFromFloat(10000)];

    // Setup grid line style
    CPTMutableLineStyle *majorXGridLineStyle = [CPTMutableLineStyle lineStyle];
    majorXGridLineStyle.lineWidth = 1.0f;
    majorXGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.25f];

    CPTMutableTextStyle *whiteTextStyle = [[[CPTMutableTextStyle alloc] init] autorelease];
    whiteTextStyle.color = [CPTColor whiteColor];    

    // Setup x-Axis.
    CPTXYAxisSet *axisSet = (CPTXYAxisSet *)graph.axisSet;
    CPTXYAxis *x = axisSet.xAxis;
    x.majorGridLineStyle = majorXGridLineStyle;
    x.labelTextStyle = whiteTextStyle;
    x.majorIntervalLength = CPTDecimalFromString(@"1");
    x.minorTicksPerInterval = 1;

    NSArray *exclusionRanges = [NSArray arrayWithObjects:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromInt(0) length:CPTDecimalFromInt(0)], nil];
    x.labelExclusionRanges = exclusionRanges;

    // Setup y-Axis.
    CPTMutableLineStyle *majorYGridLineStyle = [CPTMutableLineStyle lineStyle];
    majorYGridLineStyle.lineWidth = 1.0f;
    majorYGridLineStyle.lineColor = [[CPTColor whiteColor] colorWithAlphaComponent:0.25];

    CPTMutableLineStyle *minorYGridLineStyle = [CPTMutableLineStyle lineStyle];
    minorYGridLineStyle.lineWidth = 1.0f;
    minorYGridLineStyle.lineColor = [[CPTColor blackColor] colorWithAlphaComponent:0.1];

    CPTXYAxis *y = axisSet.yAxis;
    y.majorGridLineStyle = majorYGridLineStyle;
    y.minorGridLineStyle = minorYGridLineStyle;
    y.labelTextStyle = whiteTextStyle;
    y.majorIntervalLength = CPTDecimalFromString(@"1000");
    y.minorTicksPerInterval = 1;
    y.orthogonalCoordinateDecimal = CPTDecimalFromString(@"0");
    NSArray *yExlusionRanges = [NSArray arrayWithObjects:
                                [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(0.0) length:CPTDecimalFromFloat(0.0)],
                                nil];
    y.labelExclusionRanges = yExlusionRanges;

    // Create a high plot area
    CPTScatterPlot *highPlot = [[[CPTScatterPlot alloc] init] autorelease];
    highPlot.identifier = kHighPlot;

    CPTMutableLineStyle *highLineStyle = [[highPlot.dataLineStyle mutableCopy] autorelease];
    highLineStyle.lineWidth = 2.f;
    highLineStyle.miterLimit        = 1.0f;
    highLineStyle.lineColor = [CPTColor whiteColor];
    highPlot.dataLineStyle = highLineStyle;
    highPlot.dataSource = self;

    CPTColor *areaColor1       = [[CPTColor whiteColor] colorWithAlphaComponent:0.8f];
    CPTGradient *areaGradient1 = [CPTGradient gradientWithBeginningColor:areaColor1 endingColor:[[CPTColor whiteColor]  colorWithAlphaComponent:0.2f]];
    areaGradient1.angle = -90.0f;
    CPTFill *areaGradientFill = [CPTFill fillWithGradient:areaGradient1];
    highPlot.areaFill       = areaGradientFill;
    highPlot.areaBaseValue = [[NSDecimalNumber zero] decimalValue];
    [graph addPlot:highPlot];

    // Create the Savings Marker Plot
    selectedCoordination = 2;

    touchPlot = [[[CPTScatterPlot alloc] initWithFrame:CGRectNull] autorelease];
    touchPlot.identifier = kLinePlot;
    touchPlot.dataSource = self;
    touchPlot.delegate = self;
    [self hideTouchPlotColor];
    [graph addPlot:touchPlot];

    [pool drain];

}

- (void)hideTouchPlotColor
{
    CPTColor *touchPlotColor = [CPTColor clearColor];

    CPTMutableLineStyle *savingsPlotLineStyle = [CPTMutableLineStyle lineStyle];
    savingsPlotLineStyle.lineColor = touchPlotColor;

    CPTPlotSymbol *touchPlotSymbol = [CPTPlotSymbol ellipsePlotSymbol];
    touchPlotSymbol.fill = [CPTFill fillWithColor:touchPlotColor];
    touchPlotSymbol.lineStyle = savingsPlotLineStyle;
    touchPlotSymbol.size = CGSizeMake(12.0f, 12.0f);

    CPTMutableLineStyle *touchLineStyle = [CPTMutableLineStyle lineStyle];
    touchLineStyle.lineColor = [CPTColor clearColor];
    touchLineStyle.lineWidth = 1.0f;

    CPTMutableLineStyle *symbolLineStyle = [CPTMutableLineStyle lineStyle];
    symbolLineStyle.lineColor = [CPTColor clearColor];
    CPTPlotSymbol *plotSymbol = [CPTPlotSymbol ellipsePlotSymbol];
    plotSymbol.fill = [CPTFill fillWithColor:[CPTColor clearColor]];
    plotSymbol.lineStyle = symbolLineStyle;
    plotSymbol.size = CGSizeMake(10.0, 10.0);
    touchPlot.plotSymbol = plotSymbol;

    touchPlot.dataLineStyle = touchLineStyle;
}

// Assign different color to the touchable line symbol.
- (void)showTouchPlotColor
{
    CPTColor *touchPlotColor = [CPTColor orangeColor];

    CPTMutableLineStyle *savingsPlotLineStyle = [CPTMutableLineStyle lineStyle];
    savingsPlotLineStyle.lineColor = touchPlotColor;

    CPTPlotSymbol *touchPlotSymbol = [CPTPlotSymbol ellipsePlotSymbol];
    touchPlotSymbol.fill = [CPTFill fillWithColor:touchPlotColor];
    touchPlotSymbol.lineStyle = savingsPlotLineStyle;
    touchPlotSymbol.size = CGSizeMake(12.0f, 12.0f); 

    CPTMutableLineStyle *touchLineStyle = [CPTMutableLineStyle lineStyle];
    touchLineStyle.lineColor = [CPTColor orangeColor];
    touchLineStyle.lineWidth = 1.0f;

    CPTMutableLineStyle *symbolLineStyle = [CPTMutableLineStyle lineStyle];
    symbolLineStyle.lineColor = [CPTColor blackColor];
    CPTPlotSymbol *plotSymbol = [CPTPlotSymbol ellipsePlotSymbol];
    plotSymbol.fill = [CPTFill fillWithColor:[CPTColor orangeColor]];
    plotSymbol.lineStyle = symbolLineStyle;
    plotSymbol.size = CGSizeMake(10.0, 10.0);
    touchPlot.plotSymbol = plotSymbol;

    touchPlot.dataLineStyle = touchLineStyle;
}

// This method is call when user touch & drag on the plot space.
- (BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)point
{
    // Convert the touch point to plot area frame location
    CGPoint pointInPlotArea = [graph convertPoint:point toLayer:graph.plotAreaFrame];

    NSDecimal newPoint[2];
    [graph.defaultPlotSpace plotPoint:newPoint forPlotAreaViewPoint:pointInPlotArea];
    NSDecimalRound(&newPoint[0], &newPoint[0], 0, NSRoundPlain);
    int x = [[NSDecimalNumber decimalNumberWithDecimal:newPoint[0]] intValue];

    if (x < 0)
    {
        x = 0;
    }
    else if (x > [sampleData count])
    {
        x = [sampleData count];
    }

        selectedCoordination = x;
        if ([delegate respondsToSelector:@selector(linePlot:indexLocation:)])
            [delegate linePlot:self indexLocation:x];
        [touchPlot reloadData];

    return YES;
}

- (BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(id)event 
          atPoint:(CGPoint)point
{
    [self showTouchPlotColor];
    return YES;
}

- (BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceUpEvent:(id)event atPoint:(CGPoint)point
{
    [self hideTouchPlotColor];
    touchPlotSelected = NO;
    return YES;
}

#pragma mark - 
#pragma mark Scatter plot delegate methods

- (void)scatterPlot:(CPTScatterPlot *)plot plotSymbolWasSelectedAtRecordIndex:(NSUInteger)index
{
    if ([(NSString *)plot.identifier isEqualToString:kLinePlot]) 
    {
        touchPlotSelected = YES;
        [self applyHighLightPlotColor:plot];
        if ([delegate respondsToSelector:@selector(linePlot:indexLocation:)])
            [delegate linePlot:self indexLocation:index];
    } 
}

#pragma mark -
#pragma mark Plot Data Source Methods

- (NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot 
{
    if ([(NSString *)plot.identifier isEqualToString:kLinePlot]) 
    {
        return kNumberOfMarkerPlotSymbols;
    }
    else {
        return [sampleData count];
    }
}

- (NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index 
{
    NSNumber *num = nil;
    if ( [(NSString *)plot.identifier isEqualToString:kHighPlot] )
    {
        if ( fieldEnum == CPTScatterPlotFieldY ) 
        {
            num = [sampleData objectAtIndex:index];
        } 
        else if (fieldEnum == CPTScatterPlotFieldX) 
        {
            num = [NSNumber numberWithInt:index];
        }
    }
    else if ([(NSString *)plot.identifier isEqualToString:kLinePlot]) 
    {
        if ( fieldEnum == CPTScatterPlotFieldY ) 
        {
            switch (index) {
                case 0:
                    num = [NSNumber numberWithInt:-1000];
                    break;
                case 2:
                    num = [NSNumber numberWithInt:12700];
                    break;
                default:
                    num = [sampleData objectAtIndex:selectedCoordination];
                    break;
            }
        } 
        else if (fieldEnum == CPTScatterPlotFieldX) 
        {
            num = [NSNumber numberWithInt:selectedCoordination];
        }
    }

    return num;
}
like image 235
shebi Avatar asked May 17 '12 08:05

shebi


People also ask

What is Apple's multi-touch interface?

Apple boasts that "multi-touch" interface is: The most revolutionary user interface since the mouse. It's an entirely new interface based on a large multi-touch display and innovative new software that lets you control everything using only your fingers. So you can glide through albums with Cover Flow, flip through photos ...

What happened to multi-touch on the iPhone?

Instead it was the fact that multi-touch -- the ability of the screen to recognize two separate contacts at the same time -- had gone from experimental idea kicking around for decades to commercial rollout. Nothing would ever be the same. 2014 6/6 Plus models. Later they'd go back to the iPhone's roots with the smaller SE.

Is Apple's incremental approach to iPhone a strength or a weakness?

By then iPhone 4 was close to launching -- arguably the single-most iconic model ever with its vertical slab sides and combination of metal and glass -- but it still wouldn't be on Verizon. What should have been clear then was that Apple's incremental approach to iPhone was both a great strength and a great weakness.


1 Answers

I came across the same problem recently and couldn't find any solution. After some time researching and coding I found some solutions and want to share one, quite easy one, so it may help you get an idea how to approach this.

I have created transparent UIView, which I've put on top of CPTGraphHostingView. This view was handling needed touch events. Let's name it TestView

TestView.h file looks like

@protocol TestViewDelegate <NSObject>
- (void)myTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)myTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)myTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;

@end

@interface TestView : UIView
@property (nonatomic, weak) id <TestViewDelegate>delegate;
@end

TestView.m

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

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [self.delegate myTouchesBegan:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    [self.delegate myTouchesMoved:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    [self.delegate myTouchesEnded:touches withEvent:event];
}

TestView delegate, in my case viewController, which includes corePlot hosting view, will implement those methods and see the sample of the code below

- (void)myTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    if (touches.count == 1) {
        UITouch *touch = (UITouch *)[[touches allObjects] objectAtIndex:0];
        CGPoint point = [touch locationInView:nil];
        [self plotSpace:self.plotSpace shouldHandlePointingDeviceDraggedEvent:event atPoint:point];
    }
    if (touches.count == 2) {
        UITouch *touch = (UITouch *)[[touches allObjects] objectAtIndex:1];
        CGPoint point = [touch locationInView:nil];
        [self plotSpace:self.plotSpace shouldHandlePointingDeviceDraggedEvent:event atPoint:point];
    }
}

CPTPlotSpace delegate method in viewController will look like

- (BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)point{
    NSSet *allTouches = [event allTouches];
    if ([allTouches count] >0 ) {
        UITouch *touch1 = [[allTouches allObjects] objectAtIndex:0];
        if (touch1){
            CGPoint pointInPlotArea = [self.graph convertPoint:[touch1 locationInView:self.view] toLayer:self.graph.plotAreaFrame];
//              padding
            pointInPlotArea.x -=10;
            NSDecimal newPoint[2];
            [self.graph.defaultPlotSpace plotPoint:newPoint forPlotAreaViewPoint:pointInPlotArea];
            NSDecimalRound(&newPoint[0], &newPoint[0], 0, NSRoundPlain);
            int x = [[NSDecimalNumber decimalNumberWithDecimal:newPoint[0]] intValue];
            x--;

            if (x <= 0)
                x = 0;
            else if (x >= [self.currentDatapoints count])
                x = [self.currentDatapoints count] - 1;

            selectedCoordination = x;
            self.label.text = [NSString stringWithFormat:@"%@", [self.currentDatapoints objectAtIndex:x]];
            self.differenceLabel.text = @"";
            [touchPlot reloadData];
        }
        if ([allTouches count] > 1){
            secondTouchPlot.hidden = NO;
            UITouch *touch2 = [[allTouches allObjects] objectAtIndex:1];
            if (touch2) {
                CGPoint pointInPlotArea = [self.graph convertPoint:[touch2 locationInView:self.view] toLayer:self.graph.plotAreaFrame];
                pointInPlotArea.x -= 10;
                NSDecimal newPoint[2];
                [self.graph.defaultPlotSpace plotPoint:newPoint forPlotAreaViewPoint:pointInPlotArea];
                NSDecimalRound(&newPoint[0], &newPoint[0], 0, NSRoundPlain);
                int x = [[NSDecimalNumber decimalNumberWithDecimal:newPoint[0]] intValue];
                x--;
                if (x <= 0)
                    x = 0;
                else if (x >= [self.currentDatapoints count])
                    x = [self.currentDatapoints count] - 1;

                selectedCoordination2 = x;
                self.secondLabel.text = [NSString stringWithFormat:@"%@", [self.currentDatapoints objectAtIndex:x]];
                [secondTouchPlot reloadData];
                float first = [self.label.text floatValue];
                float second = [[self.currentDatapoints objectAtIndex:x] floatValue];
                self.differenceLabel.textColor = (first - second) > 0 ? [UIColor greenColor] : [UIColor redColor];
                self.differenceLabel.text = [NSString stringWithFormat:@"%f", first - second];

            }
        }
    }
    return YES;
}

And that's the result...

enter image description here

This is not optimized code, it's just the idea, as I mentioned above, how to approach this problem.

Hope it helps...

like image 79
ppolak Avatar answered Oct 06 '22 14:10

ppolak