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.
Related
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);
}
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 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.
What's the easiest and least performance intensive way to achieve this?
Right now i have a UIBezierPath with over 60000 lines. I want to create an image from it that will later be moved around on-screen and stretched.
Just create a graphics context and draw your path into it, like this:
UIGraphicsBeginImageContextWithOptions(path.bounds.size, NO, 0.0); //size of the image, opaque, and scale (set to screen default with 0)
[path fill]; //or [path stroke]
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
If you're planning on moving the image around, you'll need to draw it into your view, or preferably a UIImageView that's a subview of that view. This is faster than redrawing your view every time the user moves his finger.
Hope this helps!
I have a UIView with a transparent background, and some buttons. I would like to capture the drawing of the view, shrink it, and redraw (mirror) it elsewhere on the screen. (On top of another view.) The buttons can change, so it isn't static.
What would be the best way to do this?
Check a nice sample code http://saveme-dot-txt.blogspot.com/2011/06/dynamic-view-reflection-using.html following WWDC sessions. It uses
CAReplicatorLayer
for reflection, pretty easy to implement and looks really smooth and impressive.
The general idea will be to get a UIView's layer to draw itself into a context and then grab a UIImage out of it.
UIGraphicsBeginImageContext(view.frame.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You will also need to #import <QuartzCore/QuartzCore.h>
If you don't really need to capture the drawing (from what you describe, it seems unlikely that you need an image), create another instance of the view and apply a transform. Something like...
UIView* original [UIView alloc] initWithFrame:originalFrame];
UIView* copy = [[UIView alloc] initWithFrame:copyFrame];
// Scale down 50% and rotate 45 degrees
//
CGAffineTransform t = CGAffineTransformMakeScale(0.5, 0.5);
copy.transform = CGAffineTransformRotate(t, M_PI_4);
[someSuperView addSubview:original];
[someSuperView addSubview:copy];
// release, etc.
I added the rotation just to show that you can do a variety of different things with transformations.