所以我有一个精简的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一样)。
答案 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
中实现子视图。现在您的测试应用程序运行正常。