これが私の解決策です。このソリューションでscaledView
は、矢印ビューが移動するビューです。それらはspriteView
のサブクラスである クラス ですUIView
。
-(spriteView *)findArrowContainingTouch:(UITouch *)touch inView:(UIView *)scaledView atPoint:(CGPoint) touchPoint
//There could be multiple subviews whose rectangles include the point. Find the one whose center is closest.
{
spriteView * touchedView = nil;
float bestDistance2 = 9999999999.9;
float testDistance2 = 0;
for (spriteView *arrow in scaledView.subviews) {
if (arrow.tag == ARROWTAG) {
if (CGRectContainsPoint(arrow.frame, touchPoint)) {
testDistance2 = [self distance2Between:touchPoint and:arrow.center];
if (testDistance2<bestDistance2) {
bestDistance2 = testDistance2;
touchedView = arrow;
}
}
}
}
return touchedView;
}
このメソッドdistance2Between
は、2 点間の距離の 2 乗を計算します。
-(spriteView *)findArrowTouchedAtLocation:(CGPoint)p inView:(UIView *)scaledView
{
spriteView * arrow = nil;
for (spriteView *testArrow in scaledView.subviews) {
if (testArrow.tag == ARROWTAG) {
if ((p.x == testArrow.touch.x) && (p.y == testArrow.touch.y)) {
arrow = testArrow;
}
}
}
return arrow;
}
には矢印ではないサブビューがあるscaledView
ため、定数 ARROWTAG を使用して矢印を識別します。
#pragma mark - Touches
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *scaledView = [self getScaledView];
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:scaledView];
spriteView * arrow = [self findArrowContainingTouch:touch inView:scaledView atPoint:touchLocation];
if (!(arrow==nil)) {
//Record the original location of the touch event in a property, originalTouchLocation, of the arrow instance. Additionally, store the same point on the property touch.
//Both properties are necessary. The originalTouchLocation will be used in `touchesEnded` and is not available in the `touch` object. So the information is stored separately.
//The `touch` property, a CGPoint, is stored in order to identify the view. This property is updated by every touch event. The new value will be used by the upcoming event to find the appropriate view.
arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
arrow.originalTouchLocation = CGPointMake(touchLocation.x, touchLocation.y);
arrow.debugFlag = YES;
arrow.timeTouchBegan = touch.timestamp;
}
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *scaledView = [self getScaledView];
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:scaledView];
CGPoint previousLocation = [touch previousLocationInView:scaledView];
//previousLocation is used to find the right view. This must be in the coordinate system of the same view used in `touchesBegan`.
spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
if (!(arrow==nil)) {
arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *scaledView = [self getScaledView];
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:scaledView];
CGPoint previousLocation = [touch previousLocationInView:scaledView];
spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
if (!(arrow==nil)) {
arrow.touch = CGPointMake(touchLocation.x, touchLocation.y);
float strokeAngle = [self findAngleFrom:arrow.originalTouchLocation to:touchLocation];
float strokeDistance2 = sqrt([self distance2Between:arrow.originalTouchLocation and:touchLocation]);
NSTimeInterval timeElapsed = touch.timestamp - arrow.timeTouchBegan;
float newArrowSpeed = strokeDistance2 / timeElapsed / 100; //might want to use a different conversion factor, but this one works quite well
arrow.transform = CGAffineTransformMakeRotation(strokeAngle);
arrow.currentSpeed = newArrowSpeed;
}
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *scaledView = [self getScaledView];
for (UITouch *touch in touches) {
CGPoint previousLocation = [touch previousLocationInView:scaledView];
spriteView * arrow = [self findArrowTouchedAtLocation:previousLocation inView:scaledView];
if (!(arrow==nil)) {
arrow.originalTouchLocation = CGPointMake(99999.0, 99999.0);
NSLog(@"Arrow original location erased");
}
}
}