检测旋转 CAShapeLayer 上的触摸

Posted

技术标签:

【中文标题】检测旋转 CAShapeLayer 上的触摸【英文标题】:Detecting Touches on Rotated CAShapeLayer 【发布时间】:2013-07-16 14:20:58 【问题描述】:

我正在尝试在自定义 UIView 中使用自定义 CALayer 来实现 UI 元素。

基本上,用户用他/她的手指围绕一个圆圈移动滑块,所以我有一个交互层,然后是 CAShapeLayer 类型的子层,它代表滑块本身。我认为围绕圆圈移动滑块的最简单方法是仅围绕其 z 轴旋转 CAShapeLayer

虽然滑块在视觉上会按预期旋转,但当我对收到的触摸执行命中测试时,“可命中”区域仍位于滑块的预旋转位置。就好像CAShapeLayer 的旋转的视觉效果与嵌入在图层内部的UIBezierPath 分离,形成了图层的path 属性,因为我将该路径与@ 结合使用987654328@ 识别滑块上的触摸。

总的来说,我是 Core Graphics 的新手,所以我想可能有一个属性我在这里设置不正确,但我很难弄清楚它是什么。

代码如下:

TouchableLayer.m

@interface TouchableLayer ()

    CAShapeLayer *_slider;    // The interactive slider that gets moved around the circle.


-(id) initWithPosition:(NSInteger) position // Designated initializer for this layer.

    if ( self = [super init] )
    
        _slider = [CAShapeLayer layer];

        _slider.fillColor = [UIColor blackColor].CGColor;

        [self addSublayer:_slider];
    

    return self;


-(void) setFrame:(CGRect)frame

    [super setFrame:frame];

    _slider.frame = frame;

    // This path is currently hardcoded to be in the right starting spot according to
    //    other UI elements, but the magic numbers will go away once I figure out this
    //    rotation issue.
    _slider.path = [UIBezierPath bezierPathWithRect:CGRectMake(self.bounds.size.width-47, self.bounds.size.height/2-5, 30.0f, 10.0f)].CGPath;



// Checks if the given touch location was on the slider. Returns YES if it was and NO if it was not.
-(BOOL) checkSliderTouchAtPoint: (CGPoint) point

    if (CGPathContainsPoint(_slider.path , NULL, point, NO))
    
        return YES;
    
    else
    
        return NO;
    


// Purpose: Takes the given touch location, determines the angle (in radians) that it forms with respect the center of the screen,
//             and returns that angle on the interval [0-2pi] radians. [0-2pi] radians follows a positive counterclockwise path.
-(double) angleForTouchPoint:(CGPoint) point

    // We use the positive counterclockwise coordinate system in the drawing code since that's what's used traditionally
    //    outside of Apple's APIs, so multiplying the result of
    //    atan2() by -1 converts the angle from a positive clockwise unit circle to a positive counterclockwise
    //    unit circle.

    double angleInRadians = -1*atan2(point.y - (self.frame.size.height/2), point.x - self.frame.size.width/2);

    if (angleInRadians < 0) // Convert angle to 0 - 2pi radians; we want non-negative angles.
    
        angleInRadians += M_PI*2;
    

    return angleInRadians;

// points get fed into this from the UIView.
-(void) updateWithTouchAtPoint:(CGPoint) point

    if ([self checkSliderTouchAtPoint:point])
    
        double touchAngle = [self angleForTouchPoint:point];
        _slider.transform = CATransform3DMakeRotation(-M_PI, 0.0, 0.0, 1.0); // Hardcoded angle rotation for now since I need to do some subtraction later in order to determine the amount to rotate based on touches.
    

非常感谢您提供的任何帮助!

【问题讨论】:

【参考方案1】:

我通常希望事件位置以它们被传递到的 UIView 表示。由于_slider 相对于它的超层(即 UIView 的支持层)有一个转换,因此您想要使用的任何几何值都需要转换到该参考框架中。简而言之,您需要将点显式转换为_slider 的参考系。试试这样的:

-(BOOL) checkSliderTouchAtPoint: (CGPoint) point

    CGPoint pointInSliderLayer = [_slider convertPoint: point fromLayer: self.layer];
    return CGPathContainsPoint(_slider.path , NULL, pointInSliderLayer, NO);

【讨论】:

感谢您的回复,您的解决方案似乎正在运行。我仍然遇到的唯一问题是它看起来不再围绕屏幕中心旋转整个图层,而是在不沿着圆圈移动的情况下将形状旋转到位。我可以设置一个锚点或其他东西,以便沿着圆形路径旋转滑块,而无需为每次触摸手动翻译它吗? 实际上,我在 setFrame 方法中添加了一些处理边界/等的代码,这导致它不像我发布这个时那样旋转。没关系!

以上是关于检测旋转 CAShapeLayer 上的触摸的主要内容,如果未能解决你的问题,请参考以下文章

检测对子层元素的触摸

检测在 CAShapeLayer 中点击的位置

如何检测 UICollectionView 上的触摸?

Touchesbegin没有检测到被触摸的东西

如何检测 UIView 上的触摸并检测单击哪个 UIView 类似于 UIButton 上的触摸?

检测整个屏幕上的触摸