简体   繁体   English

UIView如何在视图之间转换点?

[英]How does UIView convert points between views?

Does anyone know the algorithm used by UIView's convertPoint:toView: and convertPoint:fromView: methods? 有谁知道UIView的convertPoint:toView:使用的算法convertPoint:toView:convertPoint:fromView: methods? In particular, do you know if they first convert the point to window coordinates and then to the target view's coordinate system, or might they take a short cut since the "from" view is an ancestor of the "to" view? 特别是,你知道他们是先将点转换为窗口坐标然后转换到目标视图的坐标系,还是可以采用捷径,因为“from”视图是“to”视图的祖先?

I ask because I have a loop which tests a point against a possibly large number of views and I'm wondering if it would be more efficient to convert that point to window coordinates once before the loop (which also has the advantage of not having to pass the point's view along into the loop and whatever it calls, knowing I can pass nil for the "fromView" argument) or to keep the point relative to the parent view. 我问,因为我有一个循环,测试一个点可能有大量的视图,我想知道在循环之前将该点转换为窗口坐标是否更有效(这也有一点不用将点的视图传递到循环中以及它调用的任何内容,知道我可以为“fromView”参数传递nil或者保持相对于父视图的点。

The difference may be minor but, since it makes little difference to my code, I'd rather work with the system than against it. 差异可能很小,但是,因为它对我的代码没什么影响,所以我宁愿使用系统而不是反对它。

Well, I'm not a whole lot closer to knowing what the algorithm is, but I now have some idea of what it's not doing. 嗯,我不是很了解算法是什么,但我现在已经知道它没有做什么。 I had a bit of time last night to whip up a test program to compare the methods and the results were rather surprising. 我昨晚有一点时间来制作一个测试程序来比较方法,结果相当令人惊讶。

The fastest method was pre-converting the point to window coordinates and then converting to each target view. 最快的方法是将点预转换为窗口坐标,然后转换为每个目标视图。 What was surprising was how much faster it was: about 25-27 times faster! 令人惊讶的是它的速度有多快:大约快25-27倍! And on top of that, doing a two-stage conversion each time (first to window coordinates and then a separate call to convert to the target view) was still more than 10 times faster than a single call to convertPoint:fromView: with a non-nil view argument. 最重要的是,每次进行两阶段转换(首先进行窗口坐标,然后单独调用以转换为目标视图)仍然比单次调用convertPoint:fromView:快10倍以上convertPoint:fromView: with non -nil view参数。 I really cannot account for this. 我真的无法解释这一点。 I might have expected one method to be up to twice as fast, but it really shouldn't be faster to do two conversions than to do one directly! 我可能已经预料到一种方法可以达到两倍的速度,但是进行两次转换确实不应该比直接做一次更快! (I tried the tests in various orders as well, to make sure that wasn't affecting the timings.) (我也尝试了各种顺序的测试,以确保不会影响时间。)

Here are the results (for 1 million conversions, run on an iPad 1, iOS 4.3.3): 以下是结果(100万次转换,在iPad 1,iOS 4.3.3上运行):

convert from window point: 0.204297
two-step convert through window coordinates: 0.390832
convert from subview point: 5.020129

I'll post the code below for review, but if these results are correct, my only guess is that the conversion code is highly optimized for converting to and from window/screen coordinates and that it uses some (much slower) method to convert directly between two view coordinate systems when given a non-nil view argument. 我将发布下面的代码供审阅,但如果这些结果是正确的,我唯一的猜测是转换代码是高度优化的转换到窗口/屏幕坐标和从窗口/屏幕坐标转换,它使用一些(慢得多)方法直接转换在给定非零视图参数时,在两个视图坐标系之间。 Perhaps there are conditions where the disparity is not so great (such as when there are non-identity transformations involved), but for what I expect is the common case, it seems like it would be a whole lot better for it to do the two-step conversion first to window coordinates and then to the other view. 也许存在差异不是那么大的情况(例如涉及非身份转换的情况),但是对于我所期望的常见情况,看起来这样做会好得多。 -step首先转换为窗口坐标,然后转换到另一个视图。

Again, there are probably few circumstances where this will have a noticeable affect on a program, but it's something to keep in mind if you ever have to make a lot of repeated calls to convertPoint:toView: or convertPoint:fromView: . 同样,可能很少有情况会对程序产生明显的影响,但是如果你不得不重复调用convertPoint:toView:convertPoint:fromView:

Here's my code, if anyone cares to check it over for errors or run it themselves: 这是我的代码,如果有人关心检查错误或自己运行它:

@implementation TestConvertPointViewController

- (NSTimeInterval) timeForBlock:(void (^)())block
{
    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    block();
    return CFAbsoluteTimeGetCurrent() - startTime;
}

- (void) millionTimes:(void (^)(NSUInteger))block
{
    for (NSUInteger i = 0; i < 1000000; i++) {
        block(i);
    }
}

- (void)loadView
{
    UIView* rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    UIView* subview = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 250, 500)];
    UIView* subSubview = [[UIView alloc] initWithFrame:CGRectMake(20, 10, 130, 190)];
    UIView* subSubSubview = [[UIView alloc] initWithFrame:CGRectMake(50, 10, 50, 100)];
    [subSubview addSubview:subSubSubview];
    [subview addSubview:subSubview];
    [rootView addSubview:subview];
    self.view = rootView;
}

- (void)test
{
    UIView* subview = [[self.view subviews] objectAtIndex:0];
    UIView* subSubview = [[subview subviews] objectAtIndex:0];
    UIView* subSubSubview = [[subSubview subviews] objectAtIndex:0];

    CGPoint testPoint = CGPointMake(10.0, 30.0);
    NSTimeInterval time;

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        [subSubSubview convertPoint:testPoint fromView:nil];
    } ]; } ];
    NSLog(@"convert from window point: %f", time);

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        CGPoint rootPoint = [subview convertPoint:testPoint toView:nil];
        [subSubSubview convertPoint:rootPoint fromView:nil];
    } ]; } ];
    NSLog(@"two-step convert through window coordinates: %f", time);

    time = [self timeForBlock:^{ [self millionTimes: ^(NSUInteger i){
        [subSubSubview convertPoint:testPoint fromView:subview];
    } ]; } ];
    NSLog(@"convert from subview point: %f", time);
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self test];
}

@end

I also tested it with 100 sub-sub-subviews, choosing a different one each time through, in case there was some caching going on, and the only difference could probably be accounted for in the extra overhead of the array lookups. 我还测试了100个子子视图,每次都选择不同的子视图,以防有一些缓存,并且唯一的区别可能是在数组查找的额外开销中。

So, I'm not sure what to make of this, but I know how I'm going to use convertPoint:fromView: in the future! 所以,我不知道该怎么做,但我知道我将如何使用convertPoint:fromView:将来! :-) :-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM