2013-07-16 38 views
2

我試圖在自定義UIView中使用自定義CALayer實現UI元素。檢測旋轉CAShapeLayer上的接觸

基本上,用戶用他/她的手指圍繞一個圓移動滑塊,所以我有一個用於交互的層,然後是表示滑塊本身的CAShapeLayer類型的子層。我認爲在圓周上移動滑塊最簡單的方法就是圍繞它的z軸旋轉CAShapeLayer

儘管滑塊在視覺上按預期旋轉,但是當我對接收的觸摸執行命中測試時,「可擊中」區域仍駐留在滑塊的預旋轉位置。就好像CAShapeLayer的旋轉的視覺效果與嵌入在圖層內部的UIBezierPath解耦以形成圖層的path屬性,因爲我正在使用該路徑與CGPathContainsPoint()一起識別滑塊上的觸摸。

我是新來的核心圖形一般,所以我想可能有一個屬性,我沒有在這裏設置正確,但我努力弄清楚它是什麼。

下面是代碼:

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. 
    } 
} 

我真的很感激任何幫助,您可以提供!

回答

3

我通常會期望事件位置是以它們被傳遞到的UIView的形式表示的。由於_slider相對於其超級層(即UIView的支持層)有一個轉換,因此您想要使用的任何幾何值都需要轉換爲該參考幀。簡而言之,您需要明確地將該點轉換爲_slider的參照系。嘗試這樣的:

-(BOOL) checkSliderTouchAtPoint: (CGPoint) point 
{ 
    CGPoint pointInSliderLayer = [_slider convertPoint: point fromLayer: self.layer]; 
    return CGPathContainsPoint(_slider.path , NULL, pointInSliderLayer, NO); 
} 
+0

感謝您的答覆,它看起來像你的解決方案工作。我現在唯一的問題仍然是,它看起來不再圍繞屏幕中心旋轉整個圖層,而是將圖形旋轉到位,而不會沿着圓圈移動。有沒有一個錨點或我可以設置的東西,以便沿着圓形路徑旋轉滑塊,而無需爲每次觸摸手動翻譯它? –

+0

實際上,事實證明,我添加了一些處理setFrame方法中的邊界/ etc的代碼,並導致它不像我最初發布時那樣旋轉。沒關係! –

+0

偉大! +1。 – Unheilig