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) удалите (графическую) строку, которую вы назначили этому пальцу с экрана

Надеюсь, что это поможет, я должен где-нибудь найти какой-нибудь пример кода, но сейчас я не нахожусь перед моей машиной разработки.