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
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...
This is not optimized code, it's just the idea, as I mentioned above, how to approach this problem.
Hope it helps...
I will most likely end up answering this question in multiple responses, but I will at this point begin with:
You would think Apple would have provided APIs to aid with multiple finger tracking algorithms, etc... within their Gesture Recognition objects, but they have not thus far. In the game that I developed, I needed to also have multiple fingers, up to 4 and beyond, all on screen and moving / tracking all at once. What I found was I had to implement my own finger tracking / down / up algorithms to accomplish things other than simple single finger swipes, and pinch-to-zoom operations. Lets dig in:
I beleive in your EskLinePlot.m file "should handle down events", you are going to need to implement an incrementer of "how many fingers are down". This way if you get another finger down when another is already down you will have a count of it. Similarly you are going to need to implement a decrementer in the "should handle up events" routine. In the middle of all this, will also need to be an (albeit) small database (probably an NSMutableArray) of touches. This database will be used to correlate the drag events to a finger. So how ultimately with this work. On a down event, you will: 1) create a new record in the touch database (the array number can act as your unique identifier 2) Record the new touch's current position into the (new) touch record you created in step 1 as the newest (or array position 0) position. Each touch item in the touch database should have a history of somewhere between 4 and 10 of the last positions the finger has been at (I tend to record positions as 0 = newest to 3 or 9 as the oldest). 3) Instead of simply turning on the SINGLE (graphical) line you have created, add a new (graphical) line when the down event has occured (doing the same correlation you are currently doing for screen finger position to graph numeric position)
For the "should process drag events": 1) Look at the sceen position of the finger being relayed in the drag event and correlate it to a touch within your internally held touch database (which touch in your touch database is closest to the one being presented at this time). 2) shift all previous position points for the correlated touch down by 1. 0 goes to 1, 1 goes to 2, etc... through all and of course drop the last one. 3) add the new point to the correalated touch's list of previous points as the newest point. 4) Move the (graphical) line that was correlated to that finger appropriately
For an up event: 1) correlate the position presented to a finger within your touch database. 2) remove the correlated touch from your touch database. 3) remove the (graphical) line that you had assigned to that finger from the screen
I hope this helps, I should be able to find some sample code somewhere, but I am not currently in front of my development machine.