iOs (cocos2d) screenshot cached or CCRenderTexture buffer? It doesn't change - iphone

I using tip from this ask stackoverflow.com/questions/12413460
It's work. My problem is the image not refresh while application not restart or not rebuilding.
I use method for create screenshot. I what have same functional: use touch scree, and screenshot will save into documents directory. User touch second, second screenshot save. And so. But in my app every time only first touch screenshot created, as it cached or buffer ed.
-(UIImage *) screenshotWithStartNode:(CCNode*)startNode
{
[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [CCDirector sharedDirector].winSize;
CCRenderTexture* rtx =
[CCRenderTexture renderTextureWithWidth:winSize.width
height:winSize.height];
[rtx begin];
[startNode visit];
[rtx end];
return [rtx getUIImage];
}
I call it when user touch screen (for example)
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CCNode *n = [[[CCDirector sharedDirector] runningScene].children objectAtIndex:0];
UIImage *img = [self screenshotWithStartNode:n];//this image always same by one, same when user touch screen first time
CCSprite *spriteTemp = [CCSprite spriteWithCGImage:img.CGImage key:#"image"];
[self addChild:spriteTemp];
}
On the screen items have different position in time.
First touch on the screen get right image.But all other touch create screenshots as first image and dose not change.
I think maybe it's buffer CCRenderTexture? or image cache? What i will do?
i try [img release] and try CGImageRelease(img.CGImage) after touch, but it crash app.

You're right, the previous information is being cached. Try a
[[CCTextureCache sharedTextureCache] removeTextureForKey:#"image"];
before you try to overwrite.
Or use difference key names if you need more than one image at once.

Related

frame by frame animation with UIGestureRecognizer

I would like to show an animation with images of an object rotating.
I have an NSArray with frames of the object and I would like to display them frame by frame with the property of UIImageView animationImages.
The problem is that I would like to control the animation with UISwipeGestureRecognizer (Right and Left). And depending on the speed of the gesture the object should rotate faster or slower. I think is not possible because the gesture it is called just once and not continuously.
(NOTE: this is my first question in StackOverflow)
Thank you in advance
EDIT: I just post my solution. Maybe it can be useful for anybody. I think it's useful.
Firstly: add gesture to the view.
self.recognizer = [[UISwipeGestureRecognizer alloc] init];
self.recognizer.cancelsTouchesInView=NO;
[self.view addGestureRecognizer:self.recognizer];
Secondly: in tocuhMoved method I display the frame that I need depending of the direction of the previous touch. It is called by the event of the user.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSInteger image= [self.animationView.animationImages indexOfObject:self.animationView.image];
UITouch *touch = [touches anyObject];
NSLog(#"Touch moved with index:%i", image);
if ((positionTouch.x<[touch locationInView:self.animationView].x)) {
if (image==24) {
self.animationView.image=[self.animationView.animationImages objectAtIndex:0];
}
else{
self.animationView.image=[self.animationView.animationImages objectAtIndex:image+1];
}
}
else if((positionTouch.x>[touch locationInView:self.animationView].x)){
if (image==0) {
self.animationView.image=[self.animationView.animationImages objectAtIndex:24];
}
else{
self.animationView.image=[self.animationView.animationImages objectAtIndex:image-1];
}
}
positionTouch= [touch locationInView:self.animationView];
}
Don't forget <UIGestureRecognizerDelegate>
with this method, I fill the array of frames...
-(void)addPictures{
NSMutableArray* mArray=[[NSMutableArray alloc]initWithCapacity:25];
for (int i=0; i<25; i++) {
[mArray insertObject:[UIImage imageNamed:[NSString stringWithFormat:#"Picture %i.jpg", i+1]] atIndex:i];
}
self.animationView.image=[mArray objectAtIndex:0];
self.animationView.animationImages=mArray;
[self.view addSubview:self.animationView];
}
It's actually very simple to control the speed of an animation. In fact, it's so simple (because CALayer conforms to the CAMediaTiming protocol) that the property to control it is literally called speed. It's all a matter of using the gesture as a sort of analog for a slider, and calculating an x value relative to it's location onscreen. This should be a suitable starting point:
//Speed changes necessitate a change in time offset, begin time, and
//speed itself. Simply assigning to speed it not enough, as the layer needs
//to be informed that it's animation timing function is being mutated. By
//assigning a new time offset and a new begin time, the layer
//automatically adjusts the time of any animations being run against it
layer.timeOffset = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.beginTime = CACurrentMediaTime();
layer.speed = ([self.gesture locationInView:self.view].x / CGRectGetWidth(self.view.bounds));

Lower the CPU usage of an app

I am having an issue with CPU usage. When I starts my app its animation starts at good speed and all of sudden animation speed gets lower down and then app gets crashed. But when I checked the app with Activity Monitor(Instrument) my app use CPU near about 80%-90%. I am unable reduce the CPU usage.
CODE:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location;
UITouch *touch = [[event allTouches] anyObject];
location = [touch locationInView:self.view];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location;
UITouch *touch = [[event allTouches] anyObject];
location = [touch locationInView:self.view];
touchMovedX = location.x;
touchMovedY = location.y;
[self merge];
}
// -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
//{
// [self merge];
// }
-(void)merge
{
CGSize size = CGSizeMake(320, 480);
UIGraphicsBeginImageContext(size);
CGPoint point1 = CGPointMake(0,0);
CGPoint point2 = CGPointMake(touchMovedX,touchMovedY);
UIImage *imageOne = [UIImage imageNamed:#"iphone.png"];
[imageOne drawAtPoint:point1];
UIImage *imageTwo = [UIImage imageNamed:#"Cloud.png"];
[imageTwo drawAtPoint:point2];
imageB.image=imageTwo;
UIImage *imageC = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
imageview = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,480)];
imageview.image=imageC;
[self.view addSubview:imageview];
}
-(IBAction)save:(id)sender {
UIImage* imageToSave = imageview.image;
UIImageWriteToSavedPhotosAlbum(imageToSave, nil, nil, nil);
[imageToSave release];
}
Any help would be appreciable.
Thanks
Don't call touchesMoved in your touchesBegan code. touchesMoved is called by iOS in response to touches being moved
Likewise with touchesEnded - this gets called when the user removes the finger from the screen
In addition - your code for merge is adding more and more subviews to your view - at the end of every call to merge you are callin [self.view addSubview:imageview] which is going increase your CPU usage in handling all the subviews. Everytime you move your finger in touches moved then this will be adding a new subview and never removing them.
-(void)viewDidAppear:(BOOL)animated{
UIPanGestureRecognizer *pgr1 = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(image1Moved:)];
[iv1 addGestureRecognizer:pgr1];
}
-(void)image1Moved:(UIPanGestureRecognizer *)pgr{
NSLog(#"Came here");
[iv1 setCenter:[pgr locationInView:self.view]];
}
Do some thing like the above. Where you can move the imageview.
Also, call the merge using another button, where you move the image at will, but when it is time to merge, click a button. That way, you will call Merge only once and it will not be any load on the CPU.
Looks like you are a beginner and I would highly recommend that you follow some tutorials and learn some more about
Instance Variables
Properties
gesture recognizers etc
It is not touchesMoved or touchesBegan which is causing the CPU usage. It is definitely [self merge]. I am assuming that you are doing some CPU intensive job in [self merge] and that is being done so many times.
You have to do any CPU intensive job on another thread for the app to be responsive. Also if you are doing stuff at every move, then it may become too slow.
Please post the code for what you are doing in merge method.
There are three things which you can do
Improve the merge method to make it efficient.
Use Grand Central dispatch
Read up about dispatch_async(dispatch_get_global_queue, ^{ }); etc. This will be under Grand Central Dispatch section.
Another way is to do [self performSelector:#(merge) afterDelay:0.5s];
This method will call merge, only once every 0.5 seconds.
and then if you do not want the same method to be called so many times, or it is not necessary for every move, just before that call
[NSObject cancelPreviousPerformRequestsWithTarget:<#(id)#> selector:<#(SEL)#> object:<#(id)#>
The cancel method will cancel previous invocations and call the method again.
But again, it all depends on what you are trying to do.

Zooming a background image without zooming the overlay

I'm a beginner to the iOS development, and I'm trying to display an image (a map in fact) which need to be zoomable and pannable. My problem is that I don't know how to add an overlay which will follow the pan to always represent the same location, but won't be scaled by the zoom. There will be several overlays, and each one must be clickable.
To zoom and pan the image (map), I added my UIImageView in a UIScrollView and it works great, but I have no idea how to add this feature.
I found this thread but it is not really the same problem since his overlay is static :
UIScrollview with two images - Keeping 1 image zoomable and 1 image static (fixed size)
I've developped the same application in android, and to make this work I was converting a pixel of the map in screen coordinates thanks to the transformation matrix, and I overrode the onDraw method to display it, but I don't know how to do the same on iOS, and I don't know if this is the better solution.
Thanks for any help.
Ok so I found a way to do it, if it can helps anybody :
I added my overlay in the scrollView, with the background image (the map).
+CustomScrollView
----> UIImageView (map)
----> OverlayImageView (overlay)
In order to zoom, the custom scrollview need a delegate with the following methods :
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
//The background image with the map
return mapView;
}
//When a zoom occurs, move the overlay
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
UIImageView* overlayView = [scroll.subviews objectAtIndex:1];
float x;
float y;
float width;
float height;
//keep the width and height of the overlay
width = overlayView.frame.size.width;
height = overlayView.frame.size.height;
//Move it to stay over the same pixel of the map, and centers it
x = (self.overlay.point.x * scroll.zoomScale - width/2);
y = (self.overlay.point.y * scroll.zoomScale - height/2);
overlayView.frame = CGRectMake(x,y,width,height);
}
With this, we are saying that the zoom only occurs on the background image, but as the overlay is in the UIScrollView, it pans with it. So the only thing we need to care is to move the Overlay when the zoom change, and we know it with the scrollViewDidZoom method.
To handle the touch events, we override the touchEnded:withEvent: of CustomScrollView and we forward it to the overlay if there is only one tap. I don't show the OverlayImageView since it only override this same method (toucheEnded:withEvent:) to handle a touch on it.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
// Coordinates in map view
CGPoint point = [touch locationInView:[self.subviews objectAtIndex:0]];
//forward
if(touch.tapCount == 1){
OverlayImageView* overlayView = [self.subviews objectAtIndex:1];
CGPoint newPoint = [touch locationInView:overlayView];
BOOL isInside = [overlayView pointInside:newPoint withEvent:event];
if(isInside){
[overlayView touchesEnded:touches withEvent:event];
}
}
// zoom
else if(touch.tapCount == 2){
if(self.zoomScale == self.maximumZoomScale){
[self setZoomScale:[self minimumZoomScale] animated:YES];
} else {
CGRect zoomRect = [self zoomRectForScrollView:self withScale:self.maximumZoomScale withCenter:point];
[self zoomToRect:zoomRect animated:YES];
//[self setZoomScale:[self maximumZoomScale] animated:YES];
}
[self setNeedsDisplay];
}
}
Hope this will help.
You can put an imageView on top as an overLay and set its userInteractionEnabled property to NO. Then you have to pan it programmatically.

Cocos2D iPhone - removing the black screen between CCTransition

I am using cocos2d on my app. I am doing a transition to another scene using
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFadeDown transitionWithDuration:0.5f scene:otherScene]];
On the init part of this other scene, a menu is being built, using CCMenu. This is a full screen menu.
My problem is this: the transition happens to a black screen and then the menu appears. In other words, the transition is being done before the menu is rendered, so, I see an ugly black screen for 0.5 seconds and then, after the transition is done, I see the menu.
To make things clear imagine the first scene has a picture of a boat and the second scene a picture of a car. What I have now is the boat transitioning to black and then the car pops. I need the boat transition to the car.
how do I do that? thanks.
NOTE: I have found this guy with the same problem, but I have tried that solution without success.
This is worth a try, if you are not already using it; it removes black flickers during scene loads. Run the following method before you switch scenes, in case it affects your issue (un-comment these lines and call the method directly):
- (void) removeStartupFlicker
{
//
// THIS CODE REMOVES THE STARTUP FLICKER
//
// Uncomment the following code if you Application only supports landscape mode
//
// CC_ENABLE_DEFAULT_GL_STATES();
// CCDirector *director = [CCDirector sharedDirector];
// CGSize size = [director winSize];
// CCSprite *sprite = [CCSprite spriteWithFile:#"Default.png"];
// sprite.position = ccp(size.width/2, size.height/2);
// sprite.rotation = -90;
// [sprite visit];
// [[director openGLView] swapBuffers];
// CC_ENABLE_DEFAULT_GL_STATES();
}
I have implemented a transition in my menu (same problem), call it 'fadeInView' by adding a black layer on top of everything in the init, with an opacity of 255, and in onEnter I run an action to fade the opacity to 0. as follows:
-(id) init {
self=[super init];
if(self){
// do your stuff
blackShroudLayer_=[CCLayerColor layerWithColor:ccc4(0, 0, 0, 255) width:K_SCREEN_WIDTH height:K_SCREEN_HEIGHT];
[self addChild:blackShroudLayer_ z:500];
}
return self;
}
-(void) onEnter{
// need to [super onEnter] first to that we are running
[super onEnter];
id sh = [CCFadeTo actionWithDuration:K_FADE_TIME opacity:0];
id seq = [CCSequence actions:sh,[CCCallFunc actionWithTarget:self selector:#selector(onUnshroudComplete)], nil];
[blackShroudLayer_ runAction:seq];
}
-(void) onUnshroudComplete{
[blackShroudLayer_ removeFromParentAndCleanup:YES];
}
the constants and blackShroudLayer_ are defined in the class .h file.

Add a magnifier in cocos2d games

i want to add a magnifier in cocos2d game. here is what i found online:
http://coffeeshopped.com/2010/03/a-simpler-magnifying-glass-loupe-view-for-the-iphone
I've changed the code a bit:(since i don't want to let the loupe follow our touch)
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:magnifier_rect])) {
// make the circle-shape outline with a nice border.
self.layer.borderColor = [[UIColor lightGrayColor] CGColor];
self.layer.borderWidth = 3;
self.layer.cornerRadius = 250;
self.layer.masksToBounds = YES;
touchPoint = CGPointMake(CGRectGetMidX(magnifier_rect), CGRectGetMidY(magnifier_rect));
}
return self;
}
Then i want to add it in one of my scene init method:
loop = [[MagnifierView alloc] init];
[loop setNeedsDisplay];
loop.viewToMagnify = [CCDirector sharedDirector].openGLView;
[[CCDirector sharedDirector].openGLView.superview addSubview:loop];
But the result is: the area inside the loupe is black.
Also this loupe just magnify images with the same scale, how can i change it to magnify more near the center and less near the edge? (just like real magnifier)
Thank you !!!
Here I assume that you want to magnify the center of the screen.
You have to change dynamically size attribute to your wishes according to your app needs.
CGSize size = [[CCDirector sharedDirector] winSize];
id lens = [CCLens3D actionWithPosition:ccp(size.width/2,size.height/2) radius:240 grid:ccg(15,10) duration:0.0f];
[self runAction:lens];
Cocos2d draws using OpenGL, not CoreAnimation/Quartz. The CALayer you are drawing is empty, so you see nothing. You will either have to use OpenGL graphics code to perform the loupe effect or sample the pixels and alter them appropriately to achieve the magnification effect, as was done in the Christmann article referenced from the article you linked to. That code also relies on CoreAnimation/Quartz, so you will need to work out another way to get your hands on the image data you wish to magnify.