2013-07-16 3 views
2

Я пытаюсь реализовать элемент пользовательского интерфейса, используя пользовательский CALayer внутри пользовательского UIView.Обнаружение привязок на вращающемся CAShapeLayer

В основном пользователь перемещает слайдер пальцем по кругу, поэтому у меня есть слой для взаимодействия, а затем подслой этого типа CAShapeLayer, который представляет собой сам слайдер. Я понял, что самый простой способ перемещения ползунка вокруг круга - просто повернуть CAShapeLayer вокруг своей оси z.

Хотя ползунок визуально вращается, как и ожидалось, когда я выполняю тесты на попадание при прикосновении, область «hittable» все еще находится в предварительно повернутом положении ползунка. Это как если бы визуальный эффект вращения CAShapeLayer был отделен от UIBezierPath, встроенного внутри слоя, чтобы сформировать свойство слоя path, поскольку я использую этот путь в сочетании с CGPathContainsPoint(), чтобы распознавать штрихи на слайдере ,

Я новичок в 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. 
    } 
} 

Я очень признателен за любую помощь, которую вы можете предоставить!

ответ

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

На самом деле, оказалось, что я добавил код, который касался границ/etc в методе setFrame, и это заставляло его не вращаться, как это было изначально, когда я разместил это. Неважно! –

+0

Величие !!! +1. – Unheilig

Смежные вопросы