2011-10-07 52 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對象調用的方法中,但這可能是一個昂貴的操作。不過,也許值得一試。