如何在应用CGAffineTransformRotate后移动UIImageView?

5
我正在开发一款iPhone应用程序。我有一个UIView,其中包含一组UIImageView子类对象。用户可以通过触摸拖动和旋转图像视图。但是,我在旋转后移动图像视图时遇到了麻烦。
为了旋转图像视图,我应用了一个变换旋转,这工作得很好。它看起来像这样:
CGAffineTransform trans = self.transform;
self.transform = CGAffineTransformRotate(trans, delta);

问题出现在用户尝试通过触摸移动元素时。在touchesBegan:WithEvent:方法中,我将起始点保存在类变量startLocation中:
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{   
    // Retrieve the touch point
    CGPoint pt = [[touches anyObject] locationInView:self];
    startLocation = pt;
}

在touchesMoved:withEvent:方法中,我有以下代码。如果图像视图上没有旋转变换,这段代码可以正常工作:
- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    CGPoint pt = [[touches anyObject] locationInView:self];
    CGFloat dx = pt.x - startLocation.x;
    CGFloat dy = pt.y - startLocation.y;
    CGPoint newCenter = CGPointMake(self.center.x + dx, self.center.y + dy);
    self.center = newCenter;
}

但如果图像视图上有旋转变换,那么每次touchesMoved事件中,图像视图就会在屏幕上翻滚并很快消失。在调试器中,我观察到pt的值变得极大。我意识到需要对该点进行变换,于是我这样做:

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
CGPoint pt = [[touches anyObject] locationInView:self];
if (!CGAffineTransformIsIdentity(self.transform)) {
    pt = CGPointApplyAffineTransform(pt, self.transform);
}

CGFloat dx = pt.x - startLocation.x;
CGFloat dy = pt.y - startLocation.y;
CGPoint newCenter = CGPointMake(self.center.x + dx, self.center.y + dy);
}

这个方法效果好多了。现在我可以拖动图片在屏幕上移动。但是第一次移动会导致图片在一个方向上或另一个方向上突然抖动,这取决于转换中的旋转角度和图像视图的尺寸。

如何在没有初始抖动的情况下移动图像视图?

为什么我不需要转换startLocation(即我捕捉到的触摸开始时的点)?


1
哎呀!原来我确实需要转换起始位置。一旦我这样做了,开头的奇怪震动就消失了,图像也可以平稳移动。 - richardsun
我现在也遇到了同样的问题。作为iOS的新手,我无法理解如何转换起始位置。您能否请提供一些示例代码呢? - thavasidurai
我在 iOS 中 UIGestureRecognizers 可用之前打开了这个工单。这里描述的技术和问题可能应该被忽略,而选择使用 UIGestureRecognizer 的教程,比如这个:http://www.raywenderlich.com/6567/uigesturerecognizer-tutorial-in-ios-5-pinches-pans-and-more - richardsun
感谢您的回复。我的具体要求在此链接中提到:http://stackoverflow.com/questions/12566791/how-to-rotate-and-resize-the-image-view-with-single-finger/12566902#comment16929321_12566902 我已经尝试了您的方法,但是那个教程并没有对我的要求有所帮助。 - thavasidurai
2个回答

1
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)_event {
    CGPoint pt = [[touches anyObject] locationInView:self]; 
    startLocation = pt;
} 

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)_event {
    CGPoint pt = [[touches anyObject] previousLocationInView:self];
    CGFloat dx = pt.x - startLocation.x;
    CGFloat dy = pt.y - startLocation.y;
    CGPoint newCenter = CGPointMake(self.center.x + dx, self.center.y + dy);
    self.center = newCenter;
}

- (void)setRotation:(float)rotation {
    self.transform = CGAffineTransformRotate(self.transform, degreesToRadians(rotation));
}

0

看起来您需要将坐标从未旋转的主对象转换为旋转视图系统。

请查看UIView方法"convertPoint:toView:"

convertPoint:toView:

将接收者的坐标系中的点转换为指定视图的坐标系。

- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view

参数

point

在接收器本地坐标系统(边界)中指定的点。

view

要将其坐标系转换为点的视图。如果视图为nil,则此方法会将其转换为窗口基础坐标。否则,视图和接收器必须属于同一个UIWindow对象。

返回值

已将点转换为视图的坐标系。

更新:

回应评论:

您需要获取手指触摸的“真实”坐标(在未旋转的系统中),然后当手指移动时,您始终在主非旋转视图中拥有新坐标:它们是您必须在正在移动的视图的旋转视图父级中进行转换的点。

  • 如果A是一个主视图320x480像素
  • B是一个子视图320x480像素,居中于A,
  • 并且CB中位于170,240位置的子视图(屏幕中心+10,+0
  • 并且您将B顺时针旋转90
  • 那么C仍然在B中的170,240

但您在屏幕上看到的是160,250

如果现在用户想要将其向右移动+20,则用户需在屏幕坐标系中将手指向右移动+20,而不是在B视图坐标系中移动。 因此,用户希望在屏幕的180,250位置看到它, 这意味着您需要将此点转换为B坐标系...

所以这有点简单,您只需要在用户移动手指时获取手指的屏幕坐标,然后将其转换为旋转视图坐标(B)...


谢谢你的回复,meronix。但是应用convertPoint:toView:似乎没有任何效果。我相信问题中的点已经在旋转视图系统中了。在这行代码中,self是正在移动的视图:CGPoint pt = [[touches anyObject] locationInView:self]; - richardsun
好的,没错,但你需要做相反的事情...我需要两个字符...我添加了一个新答案... - meronix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接