【问题标题】:Using pinch gestures to zoom into custom graph使用捏合手势放大自定义图形
【发布时间】:2014-01-22 00:10:36
【问题描述】:

我正在使用 CoreGraphics 从来自两个 NSArray 对象(一个包含水平数据的数组,一个包含垂直数据的数组)的数据中绘制图表。虽然这可以正常工作,但我想实现一个缩放功能,以便当用户在图表上捏合时,可视数据会根据他们的捏合进行缩放。

所以像这样捏出来:

变成这样:

我已经有了一个名为dataRange 的变量,当图形被渲染时,它被限制在这个值范围内。 dataRange 是一个NSRange,表示它由起始位置和长度指定。但是,当用户张开手指以产生缩放效果时,我不确定如何计算这些值。

在考虑了如何实现这一点之后,我想出了以下几点:

  1. 确定每个手指在屏幕上的位置,并找出我需要放大数组中的哪些元素

  2. 随着位置越来越靠近图表的两侧,相应地设置可见数据的范围

但是,这种方法存在一些问题。首先,它忽略了图表之前是否被放大过,这意味着用户不能多次放大。它的作用也与您预期的相反,因为捏合会增加可见数据的范围(缩小),而捏合则意味着缩小可见数据的范围(放大)。我想要相反的。

执行此操作的代码在 -touchesMoved:withEvent 内部实现:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    NSSet *allTouches = [event allTouches];

    if ([allTouches count] == 2) {
        CGFloat xPositionStart = 0.0f, xPositionEnd = 0.0f;
        CGFloat touchPositions[2] = {0.0f, 0.0f};

        for (UITouch *object in allTouches) {
            CGPoint touchLocation = [object locationInView:self];

            if (touchPositions[0] == 0.0f) {
                touchPositions[0] = touchLocation.x;
            } else {
                touchPositions[1] = touchLocation.x;
            }
        }

        if (touchPositions[0] > touchPositions[1]) {
            xPositionEnd = touchPositions[0];
            xPositionStart = touchPositions[1];
        } else {
            xPositionStart = touchPositions[0];
            xPositionEnd = touchPositions[1];
        }

        CGFloat xPositionStartActual = (xPositionStart / self.frame.size.width) * [self.horizontalValues count];
        CGFloat xPositionEndActual = (xPositionEnd / self.frame.size.width) * [self.horizontalValues count];

        self.dataRange = NSMakeRange(xPositionStartActual, xPositionEndActual - xPositionStartActual);
        [self setNeedsDisplay];
    }
}

我有点卡住了,因为我无法用我当前的方法找到解决这些问题的方法。如果有人能指出我做错了什么,我将不胜感激。

【问题讨论】:

  • 如果您将图形视图放在可以处理缩放的滚动视图中,不是吗?
  • 为了使有关图表的更多信息可见(而不是仅缩放图表上当前的信息,如图像),我需要根据特定数据范围重新绘制图表用户的触摸。这意味着我需要自己处理缩放。
  • 你不能根据封闭滚动视图的缩放级别来改变你的细节级别吗?然后您可以使用滚动视图的内置平移/缩放功能。滚动视图有一个委托方法:scrollViewDidZoom: 你可以使用。

标签: ios cocoa-touch uiview graph zooming


【解决方案1】:

您可以使用 UIPinchGestureRecognizer 进行自定义缩放。这就是我完成相同任务的方式:

- (void)pinchToZoom:(UIPinchGestureRecognizer *)recognizer {

    NSUInteger numTouches = [recognizer numberOfTouches], i;
    for ( i = 0; i < numTouches; ++i ) {
        CGPoint location = [recognizer locationOfTouch:i inView:self.view];
        // now you can do something with points of your pinch if you wish
    }

    // next calculate the current zoomScale TAKING INTO ACCOUNT previous value
    if (fabs(previousPinchScale - recognizer.scale) > 0.01) {

        previousPinchScale = recognizer.scale;
        zoomScale = beginScale * recognizer.scale;
        if (zoomScale < minimumCameraScale) zoomScale = minimumCameraScale;
        if (zoomScale > maximumCameraScale) zoomScale = maximumCameraScale;

        // now make the zoom using CGAffineTransform
        CGAffineTransform affineTransform = CGAffineTransformMakeScale(zoomScale, zoomScale);
        [CATransaction begin];
        [CATransaction setAnimationDuration:duration];
        [self.layer setAffineTransform:affineTransform];
        [CATransaction commit];
    }
}

现在我解释一下我使用的每个浮点属性的含义:

previousPinchScale - 一个变量,用于确定您是否在某个显着比例(大于 0.01)上捏合。这样做只是为了不多次调用 setAffineTransform,因为它的动作很慢。在你的 init 方法中设置 previousPinchScale = -1。

beginScale - 用于使自定义缩放平滑查看的变量。您在委托方法中设置它:

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if ( [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] ) {
        beginScale = zoomScale;
    }
}

zoomScale - 一个确定图形当前缩放比例的变量(如 UIScrollView 属性)。

minimumZoomScale, maximumZoomScale - 应该很明显 :)

这是一般方法,它会缩放整个图形。 如果您只想缩放其中的一部分,您可以将 affineTransform 应用于这部分,而不是整个视图。如果要实现缩放的“反向”效果,只需将所有浮点值再乘以 -1 即可。您还可以根据用户捏它的点来添加图形的一些偏移量。

我希望所有这些都能以某种方式帮助您。

祝你好运!

【讨论】:

  • 感谢@Miroslav 你的代码帮助了我。
【解决方案2】:

您不想为缩放设置动画,因为它会缩放所有内容。尝试缩放内容视图的宽度和高度约束。将您的图表视图作为内容视图放在滚动视图中。

【讨论】:

【解决方案3】:

为此的 Swift 代码。基于@Miroslav 的回答。

var previousPinchScale:CGFloat = -1.0
var zoomScale:CGFloat = 1.0
var beginScale:CGFloat = 1.0

@IBAction func pinchGestureRecognizer(_ recognizer: UIPinchGestureRecognizer) {
    if recognizer.state == .began {
        beginScale = zoomScale
    }
    
    if (abs(previousPinchScale - recognizer.scale) > 0.01) {
        
        previousPinchScale = recognizer.scale
        zoomScale = beginScale * recognizer.scale
        let minimumScale : CGFloat = 1.0
        let maximumScale : CGFloat = 10.0
        
        if (zoomScale < minimumScale) { zoomScale = minimumScale }
        if (zoomScale >= maximumScale) { zoomScale = maximumScale }
        
        /* Do whatever you want to perform with this zoomScale. 
          I used it as and index to get data from Array and did graph redraw  
        */
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多