图形上下文有问题

Posted

技术标签:

【中文标题】图形上下文有问题【英文标题】:Having trouble with graphics context 【发布时间】:2015-05-05 06:59:56 【问题描述】:

我正在开发多个终端屏幕应用程序,为此我有一个自定义的 UIView 子类用于终端视图。每次我需要一个新的终端屏幕时,我都会准备一个新的视图。

这个视图类使用CGContextRef 绘制文本。我面临的问题是上下文仅绘制创建的最后一个视图的文本,例如如果我有 3 个终端并在第一个/第二个上绘制,它仍然在第三个上绘制。

到目前为止我的代码:

-(void)drawRect:(CGRect)rect  
    contxt = UIGraphicsGetCurrentContext();



-(void)setNeedsDisplayInRect:(CGRect)rect

    UIGraphicsPushContext(contxt);

    //CGContextSaveGState(contxt);

    CGContextSetTextMatrix(contxt,CGAffineTransformIdentity);
    if (translated) 
        CGContextScaleCTM(contxt, 1, -1);
        translated = NO;
    
    CGRect rectConvert = CGRectMake(rect.origin.x, rect.origin.y-screenWindowHeight, rect.size.width, rect.size.height);

    CGContextSetFillColorWithColor(contxt, bgColor.CGColor);
    CGContextFillRect(contxt, rectConvert);

    if (!isDeleteChar) 
        CGContextSetFillColorWithColor(contxt, fgColor.CGColor);
        [displayString drawInRect:rectConvert withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];
    
    if (ul == EXTENDED_5250_UNDERLINE) 
        CGContextSetFillColorWithColor(contxt, fgColor.CGColor);
        [@"_" drawInRect:rectConvert withFont:font lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];
    

    //CGContextRestoreGState(contxt);
    UIGraphicsPopContext();

最后我自己解决了

[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]]; 紧跟在setNeedsDisplay 之后。

【问题讨论】:

这个解决方案非常糟糕。正如@HenriNormak 所指出的,您不能在setNeedsDisplayInRect 中绘图。您在drawRect: 中获取的上下文并未保证在您使用它时有效。你碰巧通过滥用 runloop 侥幸逃脱,但这会导致极其令人惊讶的副作用和死锁。 drawRect: 是唯一允许您在主上下文中绘制的位置。 @RobNapier ,你说得对,但是,我需要继续使用之前编写的代码,这就是我必须这样做的原因。 【参考方案1】:

首先,您不应该在-setNeedsDisplayRect: 方法中进行绘图,所有绘图代码都应该在drawRect: 中。这样,主运行循环可以更好地组织视图的重绘。

其次,我怀疑您用于CGRect 转换的变量有问题,并且绘制在视图边界之外。您可以通过剪切视图的绘图来测试这个前提(将视图的layer.masksToBounds 设置为YES

如果是这种情况,您可以将它们调整为相对于视图(视图内的所有绘图都应相对于其边界,而不是其框架)。绘制视图时,假设画布拉伸视图的bounds 属性,即原点(0,0)(width,height) 的大小。

现在,还值得讨论一下传递给drawRect:rect 属性到底是什么,它不能保证是视图的整个边界,所以你不应该假设。需要重绘的是视图的一部分,但是,通常的做法(对于更简单的视图)是忽略该参数并仅重绘整个视图。一旦这变得过于昂贵(或者您需要更优化的绘图),您可以考虑对视图进行部分重绘。

总而言之,如果不查看整个UIView 子类代码,就很难诊断出完整的问题。

【讨论】:

实际上代码是写在 setNeedsDisplayInRect: 中而不是 drawRect: 因为 drawRect 方法只在我们按 home 时被调用(在 ios6 中),使用另一个应用程序并返回应用程序然后内容被清除.就是这样。 是的,因为无论何时您希望系统重绘,您都必须在其上调用[view setNeedsDisplay](在您的情况下,只要影响绘图的文本或标志发生更改)。我坚持我的观点,setNeedsDisplay 如文档中所述,用于通知系统视图已脏且需要重绘,所有绘图都应在 drawRect 内进行。 是的,我同意你的观点,但这是否是问题的唯一原因?以及如何处理返回应用程序时调用 drawRect 的 iOS6 问题? 你可以收听 UIApplicationDidBecomeActiveNotification 并从那里调用 setNeedsDisplay。 "并导致在 contxt 引用的另一个视图上绘图。"这正是您在drawRect: 之外绘制所期望得到的问题。你试图绕过绘图系统,并依赖它没有承诺的东西。将您的绘图移至drawRect:。如果您需要跟踪旧绘图,请维护自己的位图上下文并绘制到其中(然后将其绘制到 drawRect: 中的主上下文中)。但是您不能像使用便笺簿那样使用主要上下文。

以上是关于图形上下文有问题的主要内容,如果未能解决你的问题,请参考以下文章

图形场景Qt中右键单击上下文菜单响应缓慢

翻译图形上下文

关于上下文的核心图形问题。 CGContextFillRects:无效的上下文 0xa88a500

空图形上下文

检测窗口是不是正在使用硬件加速的图形上下文

iOS 图形绘制(二)-UIBezierPath