泛码更新中心后,UIView帧和中心不相关?

时间:2013-04-26 10:48:53

标签: ios uiview uipangesturerecognizer

enter image description here

所以我有一个精简的ViewController示例,它重现了这种我无法理解的行为。我正在使用UIPanGestureRecognizer和一些正确允许UIView在窗口移动的代码。但是,如果我更新帧,甚至是自身,视图会跳转到意外的位置,可能是基于视图的中心。

我对此代码有两个问题。首先,为什么视图的中心是最后一次触摸的位置(基本上是映射到视图边界的anchorPoint),并且显然与视图框架的中心没有任何关系?

其次,为什么在将视图属性更改为当前值时视图会移动?在我看来,视图的中心和框架只是部分相关,我不确定我缺少什么。

此示例只是在app delegate中创建一个基本的测试视图控制器:

TestViewController *vc = [[TestViewController alloc] init];
self.window.rootViewController = vc;

TestViewController.m:

#import "TestViewController.h"
#import <QuartzCore/QuartzCore.h>

@implementation TestViewController

- (void)loadView
{
    self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 748)];
    self.view.backgroundColor = UIColor.redColor;

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
    pan.maximumNumberOfTouches = 1;
    [self.view addGestureRecognizer:pan];
}

- (void)onPan:(UIPanGestureRecognizer*)recognizer
{
    if (recognizer.state == UIGestureRecognizerStateBegan) {
        UIView *view = recognizer.view;
        CGPoint locationInView = [recognizer locationInView:view];
        CGPoint locationInSuperview = [recognizer locationInView:view.superview];

        view.layer.anchorPoint = CGPointMake(locationInView.x / view.bounds.size.width, locationInView.y / view.bounds.size.height);
        view.center = locationInSuperview;
    }

    if (recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateChanged) {

        CGPoint translation = [recognizer translationInView:[recognizer.view superview]];
        [recognizer.view setCenter:CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y+translation.y)];
        [recognizer setTranslation:CGPointZero inView:[recognizer.view superview]];
    }

    if(recognizer.state == UIGestureRecognizerStateEnded) {

        NSLog(@"AnchorPoint: %@", NSStringFromCGPoint(self.view.layer.anchorPoint));
        NSLog(@"Center: %@", NSStringFromCGPoint(self.view.center));
        NSLog(@"Frame: %@", NSStringFromCGRect(self.view.frame));

        self.view.frame = self.view.frame;
    }
}

@end

当平移手势结束时,如果我删除以下行,则平移按预期工作:

self.view.frame = self.view.frame;

我真的看不出那条线会如何导致视图四处移动,除非之前的中心属性没有“完全粘住”或什么的。此外,我不理解中心属性的价值,因为它似乎是最后触及的位置,而不是视图的中心。示例日志:

AnchorPoint: {0.204427, 0.748506}
Center: {625, 218.5}
Frame: {{14, -34}, {768, 1004}}

从长远来看,我想在这里实现的是一个可以缩放的视图,但是当滚动到边缘之外时会快速恢复(就像UIScrollView一样)。

1 个答案:

答案 0 :(得分:1)

如果删除这两行

    view.layer.anchorPoint = CGPointMake(locationInView.x / view.bounds.size.width, locationInView.y / view.bounds.size.height);
    view.center = locationInSuperview;

然后你的视图平移就像你期望的那样。你为什么要改变图层的锚点呢?一般来说,您的代码似乎过于复杂,无法实现您的目标。请参阅DavidRönnqvist,understanding the anchor point关于如何更改anchorpoint对视图的框架属性的影响(通常是无意的)。

此外,通常最好只保留顶级视图(视图控制器的self.view)并操纵子视图。这样你就知道了一切与viewController视图的关系,而不知道UIWindow正在发生什么。旋转设备时,会对该顶级视图应用旋转变换,这在检查帧数据时无效。它的子视图属性不受该转换的影响。

您的代码在未转换的纵向模式下的行为与您期望的一样,并且仅在横向或纵向倒置时抛出奇怪的结果。这些是视图已应用旋转变换的情况。 Apple表示,当对视图应用了变换时,框架的origin属性为 undefined ,因此当您执行此操作时:

self.view.frame = self.view.frame

您正在分配未定义状态的属性。期待意外。

您可以更改您的视图加载代码:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIView* subview = [[UIView alloc] initWithFrame:
                       CGRectMake(0, 0
                                  , self.view.bounds.size.width
                                  , self.view.bounds.size.height)];
    [self.view addSubview:subview];
    subview.backgroundColor = UIColor.redColor;
    [subview setAutoresizingMask:UIViewAutoresizingFlexibleWidth 
                                |UIViewAutoresizingFlexibleHeight];
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] 
                                     initWithTarget:self 
                                             action:@selector(onPan:)];
    pan.maximumNumberOfTouches = 1;
    [subview addGestureRecognizer:pan];
}

这里我们不会覆盖-loadView,单独留下来自动创建顶级视图,然后我们在viewDidLoad中实现子视图。现在您的测试应用程序运行正常。