这是我的解决方案。在此解决方案中,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
计算两点之间距离的平方。
-(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");
}
}
}