Having trouble with graphics context - ios

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.

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.

Related

Need Infinite zooming for my Drawing View which is on UIscrollView

Is it possible to zoom infinite? as i have a floor drawing functionality, i need to zoom even for a small room to be display as larger by implementing infinite zooming. If i'm zooming more than 15 times of original drawing view, drawing is disappearing and displays nothing.
Any suggestions would be appreciated!!
Here is my sample code block:
self.scrView.maximumZoomScale=200;
self.scrView.minimumZoomScale=0.5;
#pragma mark ScrollView Deleagte
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.objDrawingView;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
[self.objDrawingView.layer setContentsScale:self.objDrawingView.transform.a*[[UIScreen mainScreen] scale]];
[self.objDrawingView setNeedsDisplay];
}
If you are doing the drawing yourself in drawRect, it sounds better performance wise (and of course because of your "ignoring bogus layer" message), to have some own values containing the scale and the offset, and respecting this in your drawing code. This way the layer's size will always stay the same and there is no need to draw that huge layer if most of it is outside of the displays bounds.
Of course it shouldn't be a child of the scrollView then, so you need to get the values from scrollViewDidScroll as well to calculate your currently visible rect.
Example of the idea:
if the current code in drawRect would look like
CGContextFillRect( CGRectMake(x,y,width,height) );
and you are scaling this by scaling the whole layer, I am suggesting to have a variable containing the current scale and don't scale the whole layer, like:
CGContextFillRect( CGRectMake(x* _zoomScale,y* _zoomScale,width* _zoomScale,height* _zoomScale) );
or even better use the scale and translate methods on the CGContext then your drawing code would be independent from these. The first three lines in drawRect should then be:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, -_contentOffset.x, -_contentOffset.y);
CGContextScaleCTM(context, _zoomScale, _zoomScale);
make zoomScale and contentOffset a property and set it in the UIScrollViewDelegate methods.
Not sure if reading the transform.a is the right approach. Maybe better to use the scale variable from the method instead.

Could anyone explain drawrect and context animation?

I'm quitely new for the iOS, so I don't know much of it.
I know how to make UIView (and its childviews) but I don't know about drawRect
I make a class which inherits from UIView and make subviews in initWithFrame method.
I want to draw a NSString using CGContext after adding subViews and move it outside of view after 5 seconds.
Could anyone explain when drawRect is called and how to move it?
This code helps you to draw a NSString which was stored before in _content:
- (void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
[_content drawInRect:rectForFrame
withFont:[UIFont fontWithName:kFontMedium size:15.0f]
lineBreakMode:NSLineBreakByWordWrapping
alignment:NSTextAlignmentCenter];
}
To let the element slide out, you would have to call a NSTimer after your UIView was shown (Maybe with a delay using performSelector:withObject:afterDelay:) and change the attributes of the CGRect you want to write the NSString inside. You can just setup a method which will be called by your NSTimer in a specific interval which also updates the UIView.
- (void)updateView{
//create a rect
rectForFrame = CGRectMake....;
[self setNeedsDisplay];
}
One way to do it would be to draw the string and then start an NSTimer. Start a 5 second timer, and when it fires, then you can move it.
As for when -drawRect: is called - it's called whenever your view needs to be updated. It will be called when your view is first displayed by the OS. After that, it will usually be called when it changes size or shape, or when your code calls [myView setNeedsDisplay:YES], which tells the OS to update it.

Difference drawing on CALayer and UIView

I have one component that has an UIView subclass and a custom CAlayer into it.
In the UIView there is a circle that is drawn with CoreGraphics, and this is the code:
CGRect b = self.bounds;
int strokeSize = 2;
CGRect arcBounds = CGRectMake(b.origin.x+1, b.origin.y+1, b.size.width-2, b.size.height-2);
CGContextSaveGState(ctx); {
CGContextSetLineWidth(ctx, strokeSize);
CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
CGContextStrokeEllipseInRect(ctx, arcBounds);
} CGContextRestoreGState(ctx);
when I draw that circle in the drawRect method inside the UIView it works perfect and the circle is drawn smooth and looks great.
The problem appears when I draw another circle just over this one, but the second one is drawn in the CALayer, actually in the drawInContext method of my custom CALayer. Using just the same code the circle doesn't looks good, and have some "pixellation" on the borders.
Any clues on what can be happening? Thanks in advance.
This is due to the contentsScale property. When you have a custom CALayer the default value of this property is 1.0.
The default value of this property is 1.0. For layers attached to a
view, the view changes the scale factor automatically to a value that
is appropriate for the current screen. For layers you create and
manage yourself, you must set the value of this property yourself
based on the resolution of the screen and the content you are
providing. Core Animation uses the value you specify as a cue to
determine how to render your content. Source.
If you have a retina device and you draw with the contentsScale set to 1.0, it will result in that pixelated look you described. In order to fix this you should set the layer's contentsScale to the one of the screen.
[self.layer setContentsScale:[[UIScreen mainScreen] scale]];
This issue does not happen when you draw the circle in the drawRect method of your UIView since there the default contentsScaleFactor is already the one of the screen.
For views that implement a custom drawRect: method and are associated
with a window, the default value for this property is the scale factor
associated with the screen currently displaying the view. Source.

drawRect Doesn't want to draw anything

I have a ViewController on storyboard. I have used the interface builder to set a toolbar at the bottom of the screen. I have set the custom view to a view overrides drawRect. However, for the life of me, I cannot get anything ever to show up on screen called from that drawRect. drawRect itself is called just fine, but nothing shows up on screen.
Also, I have this ViewController with a method that uses AVCaptureSession to toggle its background to a live view from camera input. I had suspected that this might have been the cause for error, but after removing all references of AVCaptureSession, I still cannot get this to work.
Sorry for my writing and/or lack of logic, I don't have any sleep right now.
Edit: Here is a small example of code that won't work. Every method inside gets called, but nothing is to show.
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, 0,0); //start at this point
CGContextAddLineToPoint(context, 100, 100); //draw to this point
// and now draw the Path!
CGContextStrokePath(context);
}
The documentation for UIView says to not call [super drawRect] if you're overriding UIView. Remove that call! It's been known to cause strange behaviour.
According to the docs:
If you subclass UIView directly, your implementation of this method
does not need to call super. However, if you are subclassing a
different view class, you should call super at some point in your
implementation.

Draw a line in UIView based on animated CGPoint.x

I have am trying to draw a vertical line in a UIView (call it View A) based on the center of subview of "View A".
The subview animates well, however, the line drawing is not animated. it bumps to the final position.
For instance if subview is at CGPoint(0,100) and I want to animate to CGPoint(100,100).
THe subview moves as expected, however, the line drawing does not it only appears at CGPoint(100,100) throughout the animation.
Of course inside the animation block I am calling [self setNeedsDisplay]
The code I am using is as follows(the pointView is the subview mentioned above) :
CGPointView newCetner = CGPointMake(100,100);
[UIView animateWithDuration:.5 animations:^{
pointView.center = newCenter;
[self setNeedsDisplay];
}];
in the drawRect method I have the following code:
CGPathMoveToPoint(path, NULL, CGRectGetMidX(pointView.frame), pointView.center.y-100 );
CGPathAddLineToPoint(path, NULL, CGRectGetMidX(pointView.frame), pointView.center.y+100 );
CGContextAddPath(context, path);
CGContextStrokePath(context);
Any ideas how I might force the view to redraw in accordance with the animation block?
By default, animation works by storing the start and final versions of the view, and applying some inexpensive transform to move from one to the other. Intermediate versions of the view are not drawn with drawRect:.
You can request that drawRect: be called for the intermediate steps by passing the option UIViewAnimationOptionAllowAnimatedContent to animateWithDuration:delay:options:animations:completion:. Be sure that drawRect: is fast when using this option, since it will be called often.

Resources