UIView drawRect是不稳定的,即不能在屏幕上平滑滚动。我尝试过以200毫秒到500毫秒的不同间隔执行'setNeedsDisplay',似乎没什么 改善外观。这就是当剧情在屏幕上移动时他们停下来开始。 有什么方法可以改善这一点,使显示更加流畅吗?
/*==============================================================================*/
/* '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
屏幕外,然后只需用此图像替换视图内容即可。这样可以提供更好的性能,因为您可以在单独的线程中进行图像绘制(这是一个昂贵的部分),因此绘图操作不会阻止主应用程序线程,这将消除急动。
答案 1 :(得分:0)
我有一个类似的问题,我想根据触摸手势通过特定数量的弧度旋转对象。轮换是紧张的。我通过使用CADisplayLink将更新与运行循环同步来解决问题:
@property (nonatomic, strong) CADisplayLink *displayLink;
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotate)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
每次运行循环更新UI时,都会调用我的'rotate'方法。旋转平稳,我没有注意到任何表演命中。
这是一次仿射变换,这不是一项非常昂贵的操作。我想你可以将setNeedsDisplay放在一个由CADisplayLink对象调用的方法中,但这可能是一个昂贵的操作。不过,也许值得一试。