i'm not really sure if my title is really appropriate. But i'm trying to make a app that lets users draw on the screen with multitouch. If more then one touch is on the screen it will alloc custom UIview that performs all drawing and add that to the view. The problem occurs if more then 6 touches is down, then it will start too lag and fps will drop. To fix this I could make it so all drawing is made on the same view. However, this nullifies the one of the features of my app which the ability to hide some views by setting alpha to 0. So is there any way to "merge" the drawing made in these views into a separate view of some kind (layer,uiimageview or uiview) to fix the fps problems?
- (void) drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGImageRef cacheImage = CGBitmapContextCreateImage(cacheContext);
CGContextDrawImage(context, self.bounds, cacheImage);
CGImageRelease(cacheImage);
}
Related
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.
I am trying to draw on touches (mouse tap) on the pdf document generated by CGContextRef from my resource bundle. I have started from Apple zoomingpdfviewer
where I have a UIScrollView and Pdf is generated by CGContextDrawPDFPage(context, pdfPage);
I am have also added a CATiledLayer on the scrollView and it is drawn each time in layoutSubviews - when UIScrollView is zoomed. I am confused a bit that should I draw the mouse points on scrollView CGContext or TileLayers.
--> Moving ahead, I wanted to add a rect/ circle/point on the pdf document where the user taps. Just to start with I am : CGContextMoveToPoint(context, 5,5);
CGContextSetLineWidth(context, 12.0f);
CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
CGContextAddRect(context, CGRectMake(50, 50, 20, 50));
CGContextAddLineToPoint(context, 25, 5);
CGContextStrokePath(context);
drawing it to the current context. Similarly I plan to draw when user taps in. But when I zoom and TitledLayer is redrawn to match the zoom content, these drawn rect/ circle disappears. If I am not wrong then CATiledLayer is being drawn above the currentContext rects/ circle . The end functionality is quite similar to the Maps App where the Tile Layers are added but the dropped points are located exactly on the same location even after the map is zoomed. Quite lost after seeing many such posts as drawing on scrollView , pdf iOS viewer. Does anyone know how can I draw geomtry (rect/points) on the pdf document and keep their location exactly same even if PdfView Zoom in and out? Should I convert a pdf into image or any other way to do this?
After searching and trying some stuff on CoreAnimation,CALayer I came up with:
If you want to add anything on zoomable PDF viewer,
1) Add a UITapGestureRecognizer for handling single touch
UITapGestureRecognizer *singleTapGestureRecognizer=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapOnScrollView:)];
singleTapGestureRecognizer.numberOfTapsRequired=1;
singleTapGestureRecognizer.delegate=self;
[self addGestureRecognizer:singleTapGestureRecognizer];
[singleTapGestureRecognizer requireGestureRecognizerToFail:doubleTapGestureRecognizer]; // added if you have a UITapGestureRecognizer for double taps
2) Simply add the new views (circle,rect any geometry) as layers on the Tiled PDF view
[self.titledPdfView.layer addSublayer:view1.layer];
where you can subclass your geometry views to draw with CGContext currentContext. This automatically reposition the layers when pdf is zoomed in/out
I was trying to add the views by drawing context on drawLayer:(CALayer*)layer inContext:(CGContextRef)context of titledPdfView class which gave a positional error
You can also implement a TouchesMoved, TouchesEnded method if you want to draw a resizable TextView, arow, Line.
I hope this helps for someone who needs to draw customize objects on Apple's ZoomingPDFViewer.
Further to add (if anyone arrives here), replace the Apple code of TiledPdfView *titledPDFView = [[TiledPdfView alloc] initWithFrame:pageRect scale:pdfScale];
[titledPDFView setPage:pdfPage];
[self addSubview:titledPdfView];
[titledPDFView setNeedsDisplay];
[self bringSubviewToFront:titledPdfView];
self.titledPdfView = titledPDFView;
With :
titledPdfView = [[TiledPdfView alloc] initWithFrame:pageRect scale:pdfScale];
[titledPdfView setPage:pdfPage];
[self addSubview:titledPdfView];
[self bringSubviewToFront:titledPdfView];
I dont know why have they added a view like that (member object to class object) which restricts the -(void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context being before zooming. I have tried with setNeedsDisplay with no effect then replaced n it works. Esp. this is annoying if you want to draw annots on your TileLayer and nothing appears. Hope this helps!!
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.
I'd like to fill in a UIView Background with multiple colors. I want to use it as a status bar of sorts, so if 1/2 the necessary steps are completed, the UIView Background will be 1/2 green and 1/2 red. When the user completes more steps (say 2/3), more of the UIView Background turns green (2/3 in this case).
I'm guessing I need to override
-(void) drawREct: (CGRect) rect
I imagine I would get the UIView, figure out how big it is, and divide it into 2 rectangles and then fill in those rectangles.
Another option would be to add 2 UIViews programmatically, but I'm a fan of IB.
Is it possible to divy up a UIView like I want?
Thanks
This is not an IB solution, but you can subclass UIView and override the drawRect method to add a custom gradient. This will allow you to put any number of colors you like and have them transition hard or smoothly. There are many nice tutorials online that should different ways to do this (some more elaborate, some quite simple).
Another option that is fairly simple, is to override drawRect, make the background red, then fill a rectangle that takes up the bottom half of the view. It doesn't allow for fancy or smooth transitions between colors, but it's very easy to implement. For instance, something along these lines should work:
-(void)drawRect:(CGRect)rect {
CGRect upperRect = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height * percentDone);
CGRect lowerRect = CGRectMake(rect.origin.x, rect.origin.y + (rect.size.height * percentDone), rect.size.width, rect.size.height *(1-percentDone));
[[UIColor redColor] set];
UIRectFill(upperRect);
[[UIColor greenColor] set];
UIRectFill(lowerRect);
}
Here percentDone is a float (declare, property(nonatomic), synthesize) that you can tie to the user's steps. Then just update the view when the user does something by
splitView.percentDone = .5;
[splitView setNeedsDisplay];
You can smooth this out with animations as well.
An easy way would be to set the background color for your background view to, say, red. Then add another view to that background view and call that the indicator view. Size and position the indicator view to cover the background, and set its background color to green. Connect the indicator view to an outlet in your view controller, and have the view controller adjust its width (or height) as necessary to correspond with the progress of the task at hand.
Another way would be as #PengOne suggests: create a custom view, give it a 'progress' property, and override -drawRect: to draw the contents appropriately. If you're going this route, there's nothing to stop you from getting a little creative. Instead of just filling two rectangles, make the boundary between the two colors a little more interesting. You could add ripples or bubbles that, with an appropriate sound effect, might look like a container filling with liquid. Or you could do a Qix-like animation that slowly fills the screen... ;-)
I'm building a scene with Core Animation which looks similar to the screensaver on the old Apple TV. A continuous stream of images (each a CALayer) passes by vertically, from the bottom to top. To achieve this, after a layer’s animation ends when it moves out of view, it is repositioned back to the bottom, assigned a new image, and reanimated. This takes place in the animationDidStop delegate method. However, I’ve noticed that if I take a screenshot when running the app on an iPad, the layers are never repositioned to the bottom, and aren’t seen again. I've isolated the problem, and I'm certain that taking screenshots is causing it. This leads me to think that taking a screenshot has an effect on animation timing. So...
What impact does taking a screenshot on an iDevice have on animation?
Is there a better way to achieve this effect?
You could always try capturing the screenshot programmatically based upon the contents of the view, then save it as a screenshot. I do not know your objects, but this is what I have done before. All of my content for the screenshot is in CaptureView.
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIGraphicsBeginImageContext(CaptureView.frame.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor clearColor] set];
CGContextFillRect(ctx, screenRect);
[CaptureView.layer renderInContext:ctx];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIImageWriteToSavedPhotosAlbum(screenImage, nil, nil, nil);
UIGraphicsEndImageContext();
Is your animation in a stationary UIImageView? If it is the image view that you want to detect going off of the screen, just use it's frame.origin.y compared to 0.
You can use presentationLayer to capture screenshot while you animation is animating.
i.e. [CaptureView.layer presentationLayer]
you can also detect touch while animating in presentationLayer of the CALayer.