IPhone Multi Touch интерактивен для CorePlot
Я разрабатываю приложение iPhone с диаграммой основного графика. С помощью некоторых уроков мне удалось сделать это одним касанием и перетащить. Как я могу сделать это с помощью нескольких касаний и перетаскивания? Кто-нибудь, пожалуйста, помогите мне?
![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;
}
Ответы
Ответ 1
Недавно я столкнулся с той же проблемой и не нашел решения. После некоторого времени исследования и кодирования я нашел некоторые решения и хочу поделиться одним, довольно простым, поэтому он может помочь вам понять, как подойти к этому.
Я создал прозрачный UIView, который я поставил поверх CPTGraphHostingView.
Этот взгляд обрабатывал необходимые события касания.
Назовите его TestView
Файл TestView.h выглядит как
@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, в моем случае viewController, который включает представление хостинга corePlot, будет реализовывать эти методы и видеть образец кода ниже
- (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 в viewController будет выглядеть как
- (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;
}
И что результат...
![enter image description here]()
Это не оптимизированный код, это просто идея, как я уже упоминал выше, как подойти к этой проблеме.
Надеюсь, что это поможет...
Ответ 2
Я, скорее всего, отвечу на этот вопрос в нескольких ответах, но я начну с этого момента:
Вы могли бы подумать, что Apple предоставила API-интерфейсы, чтобы помочь с использованием нескольких алгоритмов отслеживания пальцев и т.д.... в своих объектах распознавания жеста, но пока их нет. В той игре, которую я разработал, мне нужно было также иметь несколько пальцев, до 4 и выше, все на экране и перемещение/отслеживание сразу. Я обнаружил, что мне приходилось выполнять собственные алгоритмы отслеживания пальцев/вниз/вверх, чтобы выполнять другие задачи, кроме простых проверок с одним пальцем, и операций с фиксированным увеличением. Позволяет выполнить поиск:
Я верю в ваш файл EskLinePlot.m "должен обрабатывать события", вам понадобится внедрить инкремент "сколько пальцев опущено". Таким образом, если вы получите еще один палец, когда другой уже отключен, у вас будет счет. Аналогичным образом вам понадобится реализовать декрементер в рутине "должно обрабатывать события". В середине всего этого также должна быть (хотя и небольшая) база данных (возможно, NSMutableArray) касаний. Эта база данных будет использоваться для корреляции событий перетаскивания с пальцем. Итак, как в конечном итоге с этой работой. В нисходящем событии вы: 1) создать новую запись в базе данных touch (номер массива может действовать как ваш уникальный идентификатор 2) Запишите новое положение текущего касания в новую запись касания, созданную на шаге 1, в качестве самой новой позиции (или позиции позиции 0). Каждый элемент касания в базе данных касания должен иметь историю где-то между 4 и 10 последних позиций, на которых находился палец (я склонен записывать позиции как 0 = самые новые до 3 или 9 как самые старые). 3) Вместо того, чтобы просто включить созданную SINGLE (графическую) линию, добавьте новую (графическую) строку, когда произошло событие down (выполняющая ту же самую корреляцию, которую вы в настоящее время выполняете для позиции экрана пальца на число цифр)
Для "должен обрабатывать события перетаскивания": 1) Посмотрите на положение sceen пальца, передаваемого в событии перетаскивания, и сопоставьте его с касанием внутри вашей внутренней базы данных касания (касание в вашей сенсорной базе данных ближе всего к тому, которое представлено в это время). 2) сдвинуть все предыдущие точки позиции для коррелированного касания вниз на 1. 0 идет до 1, 1 переходит в 2 и т.д.... через все и, конечно, отбрасывает последний. 3) добавьте новую точку в коррелированный сенсорный список предыдущих точек как самую новую точку. 4) Переместите (графическую) линию, которая была соответствующим образом соотнесена с этим пальцем.
Для события up: 1) сопоставить позицию, представленную пальцу в вашей базе данных касания. 2) удалите коррелированное касание из вашей базы данных касания. 3) удалите (графическую) строку, которую вы назначили этому пальцу с экрана
Надеюсь, что это поможет, я должен где-нибудь найти какой-нибудь пример кода, но сейчас я не нахожусь перед моей машиной разработки.