加速 -drawRect: UIView 子类的方法

Posted

技术标签:

【中文标题】加速 -drawRect: UIView 子类的方法【英文标题】:Speed up -drawRect: method for UIView subclass 【发布时间】:2012-12-14 15:23:44 【问题描述】:

我创建了一个自定义 UIView 子类。在模拟器上一切正常,但在设备上非常慢。我的-drawRect: 方法大约需要 100 毫秒才能完全重绘视图。

当我向该视图添加平移手势时出现了速度问题,并且在拖动时,该视图需要每秒重绘多次。

我需要优化 drawRect: 方法。这是我当前的-drawRect: 方法代码:

- (void)loayoutSubviews

    for (UIView* subview in self.subviews)
    
        [subview removeFromSuperview];
    ;

    // ...
    // some calculations here... assume that they can not be optimized
    // ...

    for (unsigned int blockCounter=0; blockCounter<blockQuantity; blockCounter++)
    
        NSNumber *y=[blockTops objectAtIndex:blockCounter];
        NSNumber *height=[blockHeights objectAtIndex:blockCounter];

        if ([height intValue]>2)
        
            if ([y doubleValue]>self.frame.size.height/2.0)
            
                double angle= [[self angleValueForBlockHeight:height] doubleValue];

                UIView *bView=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[blocks objectAtIndex:blockCounter]]];
                [bView setFrame:CGRectMake(0.0, [y doubleValue], self.frame.size.width, blockSize.height)];
                [bView.layer setMasksToBounds:YES];


                double oldHeight=bView.frame.size.height;

                [bView.layer setAnchorPoint:CGPointMake(0.5, 1.0)];
                [bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y+bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];

                if ([height doubleValue]!=blockSize.height)
                
                    //preparing transform
                    CATransform3D basicTrans = CATransform3DIdentity;
                    basicTrans.m34 =1.0/-projection;

                    double rangle;
                    rangle=angle/360*(2.0*M_PI);

                    bView.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);

                ;



                double newHeight=bView.frame.size.height;

                [bView setCenter:CGPointMake(bView.center.x, bView.center.y-(oldHeight-newHeight))];

                //adding subview
                [self addSubview:bView];
            
            else
                            
                double angle= [[self angleValueForBlockHeight:height] doubleValue];

                UIView *bView=[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:[blocks objectAtIndex:blockCounter]]];
                [bView setFrame:CGRectMake(0.0, [y doubleValue], self.frame.size.width, blockSize.height)];
                [bView.layer setMasksToBounds:YES];

                [bView.layer setAnchorPoint:CGPointMake(0.5, 0.0)];
                [bView setFrame:CGRectMake(bView.frame.origin.x, bView.frame.origin.y-bView.frame.size.height/2.0, bView.frame.size.width, bView.frame.size.height)];

                if ([height doubleValue]!=blockSize.height)
                
                    //preparing transform
                    CATransform3D basicTrans = CATransform3DIdentity;
                    basicTrans.m34 =1.0/-projection;

                    double rangle;
                    rangle=(360.0-angle)/360*(2.0*M_PI);

                    bView.layer.transform = CATransform3DRotate(basicTrans, rangle, 1.0f, 0.0f, 0.0f);

                ;

                //adding subview
                [self addSubview:bView];
            ;
        
        else
        
            //do not need to draw blocks with very low height
        ;
    ;

这是平移手势识别器代码:

-(void)handlePanGesture:(UIPanGestureRecognizer *)sender
        
    double translatedY = [sender translationInView:self].y;
    double delta;

    if (fabs(translatedY)<fabs(oldTranslatedY))
    
        [sender setTranslation:CGPointZero inView:self];
        oldTranslatedY=0.0;
        delta=0.0;
    
    else
    
        delta=translatedY-oldTranslatedY;
        oldTranslatedY=translatedY;
    ;

    double pOffset=delta/((blockQuantity*blockSize.height)-self.frame.size.height);

    self.scrollPosition=self.scrollPosition-pOffset;

    if (self.scrollPosition<0.0)
    
        self.scrollPosition=0.0;
    
    else if(self.scrollPosition>1.0)
    
        self.scrollPosition=1.0;
    ;

    [self setNeedsLayout];

如有任何问题,请随时问我。

更新:将代码从 drawRect: 方法移动到 layoutSubviews

【问题讨论】:

你不应该在 drawRect 中进行视图布局,我建议在 layoutSubviews 中进行 我不确定你为什么使用 drawRect。您正在创建一个 View 并使用 CATransform3DRotate 进行转换,为什么需要在 drawRect 中执行此操作? @Tom Irving,尚未重新加载页面。对不起 我会尝试将我的代码放到 layoutSubviews 方法中。并更新我的答案 将代码从 drawRect 移动到 layoutSubview 并没有提高重绘速度。 【参考方案1】:

您可以使用 Instruments 来检查哪个方法花费的时间最多,但我认为它是取消归档和创建 UIView。你为什么需要它?如果您每次都需要它们,为什么不将 UIView 保存在内存中。

【讨论】:

unarchiveObjectWithData 代码用于创建保存在内存中的视图副本。然后我更改它的锚点并为其添加转换 那么您每次都需要新的视图吗?或者你有一组静态视图?而且,为什么你需要很多视图,为什么不在一个 drawRect 中绘制它们:? OMFG!!1 这花了很多时间。我把那个改回来了。现在一切都很顺利,但我遇到了问题,因为视图被保留而不是复制。锚点问题,可能还有转换代码。你能帮我吗?只需在视图从内存中保留后添加一些代码即可重置其锚点和其他人员 我自己做了那个员工。非常感谢

以上是关于加速 -drawRect: UIView 子类的方法的主要内容,如果未能解决你的问题,请参考以下文章

与 UIView 子类相关的 UIView 元素显示在其容器之外

子类化 UIView 并显示多个实例

子类化 UIView 或使用 UIView 组合

创建 UIView 的子类 [关闭]

带有 UIView 子类的惰性属性

将 UIView 作为 SubView 添加到自定义类、UIView 的子类、Swift