在我的应用程序中,我通过子类化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");
}
}
答案 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)。如果你想进一步使用这个,你可以跳过绘制矩形之外的单元格来绘制。