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

big*_*g_m 12 uikit ios

有谁知道UIView convertPoint:toView:convertPoint:fromView:方法使用的算法?特别是,你知道他们是先将点转换为窗口坐标然后转换到目标视图的坐标系,还是可以采用捷径,因为"from"视图是"to"视图的祖先?

我问,因为我有一个循环,测试一个点可能有大量的视图,我想知道在循环之前将该点转换为窗口坐标是否更有效(这也有一点不用将点的视图传递到循环中以及它调用的任何内容,知道我可以为"fromView"参数传递nil或者保持相对于父视图的点.

差异可能很小,但是,因为它对我的代码没什么影响,所以我宁愿使用系统而不是反对它.

big*_*g_m 13

嗯,我不是很了解算法是什么,但我现在已经知道它没有做什么.我昨晚有一点时间来制作一个测试程序来比较方法,结果相当令人惊讶.

最快的方法是将点预转换为窗口坐标,然后转换为每个目标视图.令人惊讶的是它的速度有多快:大约快25-27倍!最重要的是,每次进行两阶段转换(首先进行窗口坐标,然后单独调用以转换为目标视图)仍然比convertPoint:fromView:使用非零视图参数的单次调用快10倍以上.我真的无法解释这一点.我可能已经预料到一种方法可以达到两倍的速度,但是进行两次转换确实不应该比直接做一次更快!(我也尝试了各种顺序的测试,以确保不会影响时间.)

以下是结果(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
Run Code Online (Sandbox Code Playgroud)

我将发布下面的代码供审阅,但如果这些结果是正确的,我唯一的猜测是转换代码高度优化,以转换到窗口/屏幕坐标和从窗口/屏幕坐标转换,它使用一些(慢得多)方法直接转换在给定非零视图参数时,在两个视图坐标系之间.也许存在差异不是那么大的情况(例如涉及非身份转换的情况),但是对于我所期望的常见情况,看起来这样做会好得多. -step首先转换为窗口坐标,然后转换到另一个视图.

同样,可能很少有情况会对程序产生明显的影响,但如果您不得不重复拨打convertPoint:toView:或重复调用,请记住这一点convertPoint:fromView:.

这是我的代码,如果有人关心检查错误或自己运行它:

@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
Run Code Online (Sandbox Code Playgroud)

我还测试了100个子子视图,每次都选择不同的子视图,以防有一些缓存,并且唯一的区别可能是在数组查找的额外开销中.

所以,我不知道该怎么做,但我知道convertPoint:fromView:将来我会怎么用!:-)