-1

I am working on multiple terminal screen app, for that I have a custom UIView subclass for the terminal views. Every time I need a new terminal screen, I prepare a new view.

This view class draws the text using a CGContextRef. The problem I am facing is that the context only draws the text of the last view that was created, e.g. if I have 3 terminals and drawing on first/second, it still draws on the third one.

My code so far:

-(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();
}

Finally I solved it by own using

[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]]; just after setNeedsDisplay.

Revinder
  • 291
  • 2
  • 14
  • 2
    This solution is very broken. As @HenriNormak notes, you must not draw in `setNeedsDisplayInRect`. The context that you're grabbing in `drawRect:` is not promised to be valid at the point that you're using it. You happen to be getting away with it by abusing the runloop, but this can lead to extremely surprising side-effects and deadlocks. `drawRect:` is the only place you are permitted to draw into the main context. – Rob Napier May 12 '15 at 14:28
  • @RobNapier , You are absolutely right but, I need to carry on with the earlier written code, thats why I had to this all. – Revinder May 13 '15 at 12:40

1 Answers1

2

First and foremost, you should not be doing drawing in the -setNeedsDisplayRect: method, all of your drawing code should be in drawRect: instead. This way, the main runloop can better organize redrawing of the views.

Second, I suspect the variables that you are using for your CGRect conversions are faulty and are drawing outside of the view bounds. You can test this premise by clipping the view's drawing (set layer.masksToBounds to YES for the views)

If this is the case, you can adjust them to be relative to the view (all drawing within the view should be relative to its bounds, not its frame). When drawing the view, assume a canvas that stretches the bounds property of the view, i.e origin at (0,0) and size of (width,height).

Now, it is worth also discussing what the rect property passed to drawRect: really is, it is not guaranteed to be the entire bounds of the view, so you should not assume that. It is the portion of the view that needs to be redrawn, however, common practice (for simpler views) is to ignore that parameter and just redraw the entire view. Once this becomes too expensive (or you need the drawing to be more optimal), you can look into doing partial redraws of your view.

All in all, it is difficult to diagnose the full problem without seeing the entire UIView subclass code.

Henri Normak
  • 4,695
  • 2
  • 23
  • 33
  • Actually the code is written inside setNeedsDisplayInRect: instead of drawRect: because drawRect method was getting called only (in ios6) when we press home, use another app and come back to the app then content were being cleared. Thats it. – Revinder May 06 '15 at 09:04
  • 1
    Yes, because whenever you want the system to redraw, you have to call `[view setNeedsDisplay]` on it (in your case whenever the text or the flags affecting drawing change). I maintain my point, `setNeedsDisplay` as described in the documentation, is for notifying the system that a view is dirty and needs to be redrawn, all drawing should happen within `drawRect`. – Henri Normak May 06 '15 at 13:09
  • Yes, I agree with you but does this the only cause of issue? and how can I handle that iOS6 problem of getting drawRect called on coming back to app? – Revinder May 07 '15 at 05:34
  • 1
    You could listen to UIApplicationDidBecomeActiveNotification and call setNeedsDisplay from there. – Henri Normak May 07 '15 at 06:33
  • and my concern is not about the drawRect, the actual problem i have stated in the question. – Revinder May 07 '15 at 07:11
  • I don't have the back up of screen textual data which is a complex one, that's why i used setNeedsDisplayInRect: that does not invoke drawRect:. – Revinder May 07 '15 at 07:18
  • For the misaligned drawing problem, did you try my suggestions? Mainly making sure that the rect is inside of the bounds and making sure the drawing is not happening outside of the UIView bounds? – Henri Normak May 07 '15 at 11:22
  • I am sure the drawing is well inside and working fine, the main issues was the i get the context respective to the current view only from the `drawInRect:` with `contxt = UIGraphicsGetCurrentContext();` , and then doing the drawing in `setNeedsDisplayInRect:`. The problem is that `drawInRect:` taking time to assign the contxt value, and the drawing code is executed in the mean while and result in drawing on the other view to which the contxt was referencing. – Revinder May 11 '15 at 07:54
  • 1
    "and result in drawing on the other view to which the contxt was referencing." This is exactly the problem you would expect to get by drawing outside of `drawRect:`. You're trying to bypass the drawing system, and relying on things that are not promised by it. Move your drawing to `drawRect:`. If you need keep track of old drawing, maintain your own bit map context and draw into that (and then draw that into the main context in `drawRect:`). But you can't use the main context like it's a scratchpad. – Rob Napier May 12 '15 at 14:38