UIView DrawRect вяленое мясо - PullRequest
       5

UIView DrawRect вяленое мясо

0 голосов
/ 07 октября 2011

UIView drawRect является рывком, т. Е. Не плавно прокручивается по экрану.Я пытался выполнить 'setNeedsDisplay' с различными интервалами от 200 до 500 мс, и ничто не улучшило внешний вид.То есть, когда графики перемещаются по экрану, они останавливаются и запускаются.Можно ли как-нибудь улучшить это, чтобы сделать дисплей более плавным?

/*==============================================================================*/
/* '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================================*/

Ответы [ 2 ]

1 голос
/ 07 октября 2011

Имейте в виду, что рисование происходит только в основном потоке, что означает, что во время работы drawRect: весь ваш пользовательский интерфейс блокируется. Если этот код рисования медленный, то ваше приложение не будет отвечать на запросы во время рисования. Исходя из размера кода и количества отдельных операций рисования, которые вы выполняете в этом методе, похоже, что именно здесь и происходит.

drawRect: получил свое название от того факта, что вы должны ограничить свой рисунок областью, описанной rect. Из вашего вопроса не на 100% ясно, меняется ли каждый раз весь график или добавляется лишь немного новых данных. Если есть способ, которым вы можете реструктурировать свой код так, чтобы не нужно было каждый раз перерисовывать все это, это почти наверняка устранит ваши проблемы с рывками.

Например, вы также можете избежать рисования линий сетки при каждом обновлении, поместив их в отдельный вид за видом, который содержит график. Линии сетки (я полагаю) не нужно перерисовывать очень часто, поэтому, если вы сделаете вид графика прозрачным, UIKit объединит их для вас, и вы сможете избежать операции рисования. Возможно небольшая экономия, но все помогает. Это также означает, что вы можете стереть свое представление, заполнив его [UIColor clearColor]. Рисование старого графика с цветом фона - чрезвычайно дорогая операция, но заливка прямоугольника очень дешева. Это меньше кода и он работает быстрее.

Если этого недостаточно, вы также можете сделать свой рисунок на отдельном UIImage закадровом экране, а затем просто заменить содержимое вашего представления этим изображением. Это даст вам лучшую производительность, потому что вы сможете выполнять рисование изображения (которое является дорогой частью) в отдельном потоке, и поэтому операция рисования не будет блокировать основной поток приложения, что устранит рывки.

0 голосов
/ 03 апреля 2013

У меня была похожая проблема, когда я хотел вращать объект через определенное количество радиан, основываясь на жесте касания.Вращение было нервным.Я решил проблему, используя CADisplayLink для синхронизации обновлений с циклом выполнения:

@property (nonatomic, strong) CADisplayLink *displayLink;

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

Каждый раз, когда цикл запуска обновлял пользовательский интерфейс, вызывался мой метод rotate.Вращение было плавным, и я не заметил каких-либо скачков производительности.

Это было для аффинного преобразования, которое не очень дорогая операция.Я полагаю, вы могли бы поместить setNeedsDisplay в метод, который вызывается объектом CADisplayLink, но это, вероятно, будет дорогостоящей операцией.Тем не менее, возможно, стоит попробовать.

...