如何改善糟糕的drawRect性能?

时间:2014-02-15 01:27:51

标签: ios objective-c drawing drawrect

在我的应用程序中,我通过子类化UIView创建一个网格视图。如果用户触摸网格的单元格,则它在许多不同的状态之间切换,例如, 'ON'或'OFF'并相应地改变颜色。因此,当触摸单元格时,我调用setNeedsDisplay来刷新视图。

在我的新iPhone 5s上运行得很好但在iPhone 4上表现糟糕 - 我的整个应用程序冻结,音频口吃等。有更好的方法吗?如何提高绩效?

 /**
 * Draw rect
 */
-(void)drawRect:(CGRect)rect
{
    _cellWidth = _frameWidth / _gridSizeX;
    _cellHeight = _frameHeight / _gridSizeY;

    int firstCellY = _frameHeight - _cellHeight;

    for(int x = 0; x < _gridSizeX; x++) {
        for(int y = 0; y < _gridSizeY; y++) {

            CGRect rect = CGRectMake(x * _cellWidth, firstCellY - (y * _cellHeight), _cellWidth, _cellHeight);

            UIColor *strokeColor = [UIColor grid];
            UIColor *fillColor;

            if(currentBeat != x) {
                switch(mCells[x][y]) {
                    case OFF:
                        //bar lines
                        if(x % 4 == 0) {
                            fillColor = [UIColor bgBar];
                        }
                        //octave lines
                        else if(scaleLength > 0 && (y % scaleLength == 0 || y == _gridSizeY-1)) {
                            fillColor = [UIColor bgOctave];
                        }
                        else {
                            fillColor = [UIColor bgMain];
                        }
                        break;
                    case ON:
                        fillColor = [UIColor cellOn];
                        break;
                    case ON_EXTEND:
                        fillColor = [UIColor cellOnExtend];
                        break;
                    case LOCKED:
                        fillColor = [UIColor cellLocked];
                        break;
                    case LOCKED_EXTEND:
                        fillColor = [UIColor cellLockedExtend];
                        break;
                    default:
                        break;
                }
            }
            else {
                switch (mCells[x][y]) {
                    case OFF:
                        if(_playing) {
                            fillColor = [UIColor bgMainHL];
                        }
                        else {
                            fillColor = [UIColor bgBar];
                        }
                        break;
                    case ON:
                        if(_playing) {
                            fillColor = [UIColor cellOnHL];
                        }
                        else {
                            fillColor = [UIColor cellOn];
                        }
                        break;
                    case ON_EXTEND:
                        fillColor = [UIColor cellOnExtendHL];
                        break;
                    case LOCKED:
                        if(_playing) {
                            fillColor = [UIColor cellLockedHL];
                        }
                        else {
                            fillColor = [UIColor cellLocked];
                        }
                        break;
                    case LOCKED_EXTEND:
                        fillColor = [UIColor cellLockedExtendHL];
                        break;
                    default:
                        break;
                }
            }

            CGContextRef context = UIGraphicsGetCurrentContext();
            CGContextSetFillColorWithColor(context, fillColor.CGColor);
            CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
            CGContextFillRect(context, rect);
            CGContextStrokeRect(context, rect);
        }
    }
}


/**
 * Touch events
 */
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self handleTouches:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self handleTouches:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self handleTouches:touches withEvent:event];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self handleTouches:touches withEvent:event];
}


/**
 * Handle touches
 */
-(void)handleTouches:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint location = [touch locationInView:self];

    //Find column and row
    int col = (int) (location.x / _cellWidth);
    int row = (int) _gridSizeY - (location.y / _cellHeight);

    //Only redraw view when row and column changed, so doesn't redraw every touch that's registered
    if([touch phase] == UITouchPhaseBegan || (touchBeganCol != col || touchBeganRow != row)) {
        [self.delegate mViewTouchEventWithTouch:touch andRow:row andColumn:col];
        [self setNeedsDisplay];
        touchBeganCol = col;
        touchBeganRow = row;
        NSLog(@"redraw");
    }
    else if([touch phase] == UITouchPhaseEnded || [touch phase] == UITouchPhaseCancelled) {
        [self.delegate mViewTouchEventWithTouch:touch andRow:row andColumn:col];
        [self setNeedsDisplay];
        touchBeganCol = 99999;
        touchBeganRow = 99999;
        NSLog(@"redraw");
    }
}

2 个答案:

答案 0 :(得分:1)

我在另一篇文章中提到了一些建议,并使用了CALayers而不是drawRect。

/**
 * Draw layers
 */
-(void)drawLayers
{
    for(CellLayer *layer in layerArray) {
        [layer removeFromSuperlayer];
    }
    [layerArray removeAllObjects];

    _cellWidth = _frameWidth / _gridSizeX;
    _cellHeight = _frameHeight / _gridSizeY;

    int firstCellY = _frameHeight - _cellHeight;

    UIColor *strokeColor = [UIColor grid];

    for(int x = 0; x < _gridSizeX; x++) {
        for(int y = 0; y < _gridSizeY; y++) {

            CellLayer *cellLayer = [CellLayer layer];
            cellLayer.frame = CGRectMake(x * _cellWidth, firstCellY - (y * _cellHeight), _cellWidth, _cellHeight);

            cellLayer.backgroundColor = [self getColourForState:mCells[x][y] andX:x andY:y].CGColor;
            cellLayer.borderColor = strokeColor.CGColor;
            cellLayer.borderWidth = 0.5;
            cellLayer.col = x;
            cellLayer.row = y;
            cellLayer.state = mCells[x][y];

            [layerArray addObject:cellLayer];
            [self.layer addSublayer:cellLayer];
        }
    }
}

然后按照此处的建议,在触摸事件之后,我只更改需要更新的单元格的属性:

- (void)redrawChangedCells
{
    [CATransaction setDisableActions:YES];
    [[layerArray copy] enumerateObjectsUsingBlock:^(CellLayer *layer, NSUInteger idx, BOOL *stop) {
        if(mCells[layer.col][layer.row] != layer.state) {
            layer.backgroundColor = [self getColourForState:mCells[layer.col][layer.row] andX:layer.col andY:layer.row].CGColor;
            layer.state = mCells[layer.col][layer.row];
            [self updateLayerArrayWithLayer:layer andIndex:idx];
        }
    }];
}

答案 1 :(得分:0)

有一些事情可以帮助:
根据经验,您应该尽可能避免在绘图代码中进行对象分配,因为这非常昂贵。在你的代码中,下一行可以移到double-for循环之外,只需重用变量。

       UIColor *strokeColor = [UIColor grid];
       UIColor *fillColor;

要扩展之前的注释,这是我不确定的事情,甚至像[UIColor bgBar]这样的调用实例化对象也是可能的。我通常有类变量,例如_bgBarColor,已经设置了适当的颜色。

另一个有用的提示是使需要重绘的rect无效(必须有类似setNeedsDisplay:Rect的东西来代替setNeedsDisplay)。如果你想进一步使用这个,你可以跳过绘制矩形之外的单元格来绘制。