Обнаружение касаний на повернутом CAShapeLayer
Я пытаюсь реализовать элемент пользовательского интерфейса, используя пользовательский CALayer
внутри пользовательского UIView
.
CAShapeLayer
, который представляет сам слайдер. Я решил, что самый простой подход к перемещению ползунка по кругу - это просто повернуть CAShapeLayer
вокруг его оси Z.
Хотя слайдер визуально вращается, как и ожидалось, когда я выполняю хит-тесты при полученных касаниях область "hittable" по-прежнему находится в предварительно повернутом положении ползунка. Это как если бы визуальный эффект вращения CAShapeLayer
был отделен от UIBezierPath
, встроенного в слой, чтобы сформировать свойство path
слоя, поскольку я использую этот путь в сочетании с CGPathContainsPoint()
для распознавания прикосновений к слайдеру.
Я новичок в графическом ядре в целом, поэтому я думаю, что здесь может быть свойство, которое я не устанавливаю правильно, но я борюсь чтобы выяснить, что это такое.
Вот код:
Сенсорный слой.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); }