2011-10-07 62 views
-1

UIView drawRect是不平滑的,即不能平滑地在屏幕上滚动。我曾尝试以不同的时间间隔从200毫秒到500毫秒执行'setNeedsDisplay',并且似乎没有任何内容可以改进外观。那是因为地块在屏幕上移动并停止并开始。 有没有什么办法可以改善这一点,让显示更流畅?UIView drawRect是生涩的

/*==============================================================================*/ 
/* 'drawRect' - override function.           */ 
/*                    */ 
/* This function is responsible for plotting cardiac waveforms (ECG) and also */ 
/* erases any prior ECG plot values that may be currently visible on the  */ 
/* screen.          */ 
/*           */ 
/* It does this indirectly via 'EcgThread' or 'ECGErase' which send the  */ 
/* 'setNeedsDisplay' message which triggers the 'drawRect' callback function. */ 
/* Once an ECG is started 'EcgThread' controls the frequency of 'drawRect'  */ 
/* being by simple timer. (See 'EcgThread' for current timer value.)  */  
/*           */ 
/*==============================================================================*/ 
/* Here's how the entire ECG process works:      */ 
/*           */ 
/* 1) User starts ECG which causes a 'send_ecg' command to be sent to  */ 
/* 'CommThread'.         */ 
/*           */ 
/* 2) 'CommThread' the sends the 'send_ecg' command to the Pacemaker and then */ 
/*  sends itself the 'read_ecg' command.*/      
/*           */ 
/* 3) The Pacemaker then starts acquiring 64 cardiac A/D samples (10-bits) per */ 
/* second and sends them back to 'CommThread'.     */ 
/*           */ 
/* 4) As samples are received by 'CommThread' (via 'read_ecg') on a streaming */ 
/* basis they are stored in 'ECGY' array (vertical plots) in reverse order */ 
/* i.e., from top to bottom (currently room for 128 samples).   */ 
/*           */ 
/* 5) 'EcgThread' runs continuously on a timer basis sending 'setNeedsDisplay' */ 
/* message which causes 'iOS' to perform callbacks to 'drawRect' who is  */ 
/* responsible for drawing the cardiac (plots) waveforms from left to right */ 
/* across (horizontally) the screen.      */ 
/*           */ 
/* 6) 'drawRect' processes 'ECGY' bottom to top (opposite of 'CommThread') and */ 
/* each draw loop (plotting new values) takes about 13-millseconds. This is */ 
/* necessary because not doing so results in drawing upside down plots.  */ 
/*           */ 
/* 7) User stops ECG and 'TimerProc' sends a series of 'send_ecgstop' commands */ 
/* to 'CommThread' who in turn sends the 'send_ecgstop' commands to the PM. */ 
/* The reason why we send multiple 'send_ecgstop' is due to the streaming */ 
/* nature of the sending ECG samples and getting the PM to 'listen' to us. */ 
/* Usually stopping will go smoothly. Occasionally it may be necessary to */ 
/* move the Wand away, wait a few seconds and place Wand back over patient's */ 
/* chest (causing an interrupt) before normal operation returns.   */ 
/*           */ 
/*==============================================================================*/ 
- (void) drawRect : (CGRect) rect            // Callback routine 
{ 
int i, ii, x, xx, y;               // Local array indices 

fEcgDraw = YES;                 // Show 'drawRect' is running 
[super drawRect : rect];              // Call standard handler 
CGContextRef context = UIGraphicsGetCurrentContext();       // Get graphics context 
CGContextSetAllowsAntialiasing (context, NO);         // Turn off anti-alliaing 
CGContextSetLineWidth (context, 1);            // Set width of 'pen' 
/*==============================================================================*/ 
/* 'HH' is used as a height bias in order to position the waveform plots in  */ 
/* middle of the view (screen).             */ 
/*==============================================================================*/ 
HH = 424;                  // Force height bias for now 
if (fEcgErase == YES)               // Show we erase the view? 
{ 
    CGContextSetStrokeColorWithColor (context,         // Set color of 'pen' 
             [UIColor blackColor].CGColor);   // Black (to erase) 
/*==============================================================================*/ 
/* Erase the last screen.              */ 
/*==============================================================================*/ 
    for (i = 0, x = 0; i < 127; i++)           // Iterate for all array elements 
    {  
     CGContextMoveToPoint (context,           // Update current position to specified point 
           ECGX[x],           // Starting X-coordinate 
           (HH - ECGS[x]));         // Starting Y-coordinate (with height bias) 
     CGContextAddLineToPoint (context, ECGX[(x + 1)],      // Draw line from current position 
           (HH - ECGS[((x + 1) % 127)]));     // Ending Y-coordinate (with height bias) 
     x++;                // Step to next array element 
    } // end - for (i = 0; i < 127; i++) 
    CGContextClosePath (context);            // Close current path 
    CGContextStrokePath (context);           // Stroke current path (paint the path) 
    fEcgErase = NO;               // Reset erase flag 
} // end - if (fEcgErase == YES) 
else if (fECGLOOP)               // Did request come from 'EcgThread'? 
/*==============================================================================*/ 
/* Draw ECG cardiac waveforms on view.           */ 
/*==============================================================================*/ 
{ 
    xx = 1;                 // Counts markers 
    x = 0;                 // Reset horizontal axis 
    y = YY;                // Use saved startimg ECGY[] index 
    ii = 0;                 // Initialize marker count 
    #define GRIDSIZE 12              // Grid width in pixels 
    int width = rect.size.width;           // Get the view width 
    int height = rect.size.height;           // Get the view height 
/*==============================================================================*/ 
/* First draw a grid pattern to draw ECG waveforms into.      */ 
/*==============================================================================*/ 
    CGContextSetStrokeColorWithColor (context,         // Set color of 'pen' 
             [UIColor lightGrayColor].CGColor); // Use 'light gray' for grid pattern 
    for (i = 0; i <= width; i = i+GRIDSIZE)         // First the vertical lines 
    { 
     CGContextMoveToPoint (context, i, 0);        // Update current position to specified point 
     CGContextAddLineToPoint (context, i, height);       // Draw line from current position 
    }  // end - for (i = 0; i <= width; i = i+GRIDSIZE) 
    for (i = 0 ; i <= height; i = i+GRIDSIZE)         // Then the horizontal lines 
    { 
     CGContextMoveToPoint (context, 0, i);        // Update current position to specified point 
     CGContextAddLineToPoint (context, width, i);      // Draw line from current position 
    } // end - for (i = 0 ; i <= height; i = i+GRIDSIZE) 
    CGContextClosePath (context);            // Close current path 
    CGContextStrokePath (context);           // Stroke current path (paint the path) 
/*==============================================================================*/ 
/* Now draw (plot) cardiac waveforms using using pre-stored ECG sample values. */ 
/*==============================================================================*/ 
    for (i = 0; i < 127; i++)            // Iterate for number ECGY[] entries 
    { 
/*==============================================================================*/ 
/* Erase the prior ECG A/D plot value.           */ 
/*==============================================================================*/ 
#if 0 // NOT NEEDED CUZ WE SELECTED CLEAR CONTEXT IN EcgViewController.xib    
     CGContextSetStrokeColorWithColor (context,        // Set color of 'pen' 
              [UIColor blackColor].CGColor); // Black to erase old plot 
     CGContextMoveToPoint (context,          // Update current position to specified point 
           ECGX[x],          // Starting X-coordinate of prior position 
           (HH - ECGS[x]));        // Starting Y-corrdinate with height bias 
     CGContextAddLineToPoint (context,          // Draw line from current position 
           ECGX[(x + 1)],        // Ending X-coordinate 
           (HH - ECGS[((x + 1))]));     // Ending Y-coordinate using saved Y-axis (with height bias) 
     CGContextClosePath (context);           // Close current path 
     CGContextStrokePath (context);          // Stroke current path (paint the path) 
#endif // NOT NEEDED CUZ WE SELECTED CLEAR CONTEXT IN EcgViewController.xib    
/*==============================================================================*/ 
/* Plot the next ECG A/D plot value.           */ 
/*==============================================================================*/ 
     CGContextSetStrokeColorWithColor (context,        // Set color of 'pen' 
              [UIColor greenColor].CGColor); // White to draw new plot 
     CGContextMoveToPoint (context,          // Update current position to specified point 
           ECGX[x],          // Starting X-coordinate of new position 
           (HH - ECGY[y]));        // Starting Y-coordinate with height bias 
     CGContextAddLineToPoint (context,         // Draw line & prevent overrun 
           ECGX[(x + 1)],        // Ending X-coordinate 
           (HH - ECGY[((y + 1) % 127)]));    // Ending Y-axis (with height bias) 
     CGContextClosePath (context);           // Close current path 
     CGContextStrokePath (context);          // Stroke current path (paint the path) 
     ECGS[x] = ECGY[y];             // Save last plot value for erase 
     x++;                // Next ECGX[] (y-axis) plot value index 
/*==============================================================================*/ 
/* Below as we increment 'y' it will eventually roll to zero and when we get */ 
/* to the end of the above 'for' loop 'y' will have its starting value.   */    
/*==============================================================================*/ 
     y = ((y + 1) % 127);            // Next ECGY[] (y-axis) plot value & prevent overrun 
     ulPlots++;               // Count number of plots 
    } // end - for (i = 0; i < 127; i++) 
    y = ((y + 16) % 127);            // Next starting y-axis 'ECGY' index 
    YY = y;                 // Save it for next iteration 
    EcgCount = 0;               // Reset skip count (inc. by 'CommThread' 
} // end - if (fEcgErase == YES) 
// UIGraphicsPopContext(); 
fEcgDraw = NO;                // Show 'drawRect' not running 
// [NSThread sleepForTimeInterval : 0.1];          // Delay a little 

} // end - 'drawRect' 
/*===============================END OF FUNCTION================================*/ 
+0

不要只是粘贴一堆代码,并询问如何提高性能。首先解释代码试图做什么,然后解释你所尝试过的。你做了什么测量?他们告诉你什么? –

回答

1

保持在主线程,这意味着,虽然drawRect:运行,整个UI块上绘制只有没有发生过的想法。如果此绘图代码较慢,则在绘图发生时,您的应用程序将无响应。根据代码的大小以及您在此方法中执行的单个绘图操作的数量,这似乎是这里发生的事情。

drawRect:得名于你应该限制你的图纸到rect所描述的区域。如果整个图形每次都改变,或者只添加一点点新数据,那么从您的问题中就不能100%清楚。如果有一种方法可以重新构建代码,那么每次都不需要重新绘制整个代码,这几乎可以肯定会消除您的匆忙问题。

例如,您也可以避免在每次刷新时绘制网格线,方法是将它们放在包含图的视图后面的单独视图中。网格线(我猜)不需要经常重绘,所以如果你使绘图视图透明,UIKit会为你合成它们,你可以避免绘图操作。可能是一小笔储蓄,但任何帮助。这也意味着你可以通过填写[UIColor clearColor]来清除你的视图。用背景色绘制旧图是一种非常昂贵的操作,但矩形填充很便宜。它代码少,运行速度更快。

如果这还不够,您还可以将其绘制到单独的UIImage屏幕外,然后用您的图像替换视图中的内容。这会给你更好的性能,因为你可以在单独的线程中完成图像绘制(这是昂贵的部分),所以绘图操作不会阻塞主应用程序线程,从而消除这种急躁。

0

我有一个类似的问题,我想旋转基于触摸手势的特定数量的弧度的对象。轮换是紧张的。我用CADisplayLink同步与运行循环更新解决了这个问题:

@property (nonatomic, strong) CADisplayLink *displayLink; 

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotate)]; 
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

每次运行的循环更新UI,我的“旋转”方法被调用。轮换很顺利,我没有注意到任何性能命中。

这是一个仿射转换,这不是一个非常昂贵的操作。我想你可以把setNeedsDisplay放在一个由CADisplayLink对象调用的方法中,但这可能是一个昂贵的操作。不过,也许值得一试。