How to add text on moving sprites in Cocos2D? - objective-c

This is my first game in Cocos2D. I am using Cocos2D 1.0.1. I want to add text on moving sprites which should be center aligned. I've taken a CCLabelTTF with a text on it but I cannot make it center aligned. This is what I've done so far:-
-(void)addTarget {
int enType= arc4random() % 11;
CCSprite *target=[CCSprite spriteWithFile:[NSString stringWithFormat:#"balloon%d.png",enType] rect:CGRectMake(0, 0, 100, 119)];
label = [[CCLabelTTF alloc] initWithString:#"H!" dimensions:CGSizeMake([target contentSize].width, [target contentSize].height)
alignment:UITextAlignmentCenter fontName:#"verdana" fontSize:20.0f];
label.color = ccc3(60,60,60);
[target addChild:label z: 10];
// Create the actions
id actionMove = [CCMoveTo actionWithDuration:rangeDuration position:ccp(actualX,winSize.height+target.contentSize.height)];
[target runAction:[CCSequence actions:actionMove, nil]];
//[label setPosition:target.position];
// Add to targets array
[targets addObject:target];
}
Somewhere I've read that adding "[label setPosition:target.position];" in action of sprite will make it center aligned but in vain.

Try setting your label position here instead:
label = [[CCLabelTTF alloc] initWithString:#"H!" dimensions:CGSizeMake([target contentSize].width, [target contentSize].height)
alignment:UITextAlignmentCenter fontName:#"verdana" fontSize:20.0f];
//LABEL POSITION HERE
label.position = ccp(0, 40);
label.color = ccc3(60,60,60);
You may have to play with the position values until you get it where you want.

You don't need to change position of the label, everything is placed based on the center of the image. Also, with this
[target addChild:label z: 10];
try setting the button to z: 11, and keep the label at z:10

Related

SKLabelNode sometimes disappear in iOS 9.2

I have a node with a picture of a box added right on top of another node with image of an airship:
SKSpriteNode *box = [SKSpriteNode spriteNodeWithImageNamed:#"Box"];
box.position = airshipNode.position;
box.physicsBody = [SKPhysicBody bodyWithRectangleOfSize:box.size];
box.physicsBody.categoryBitMask = 1;
box.physicsBody.collisionBitMask = 0;
box.zPosition = airship.zPosition - 1;
[box setScale:0.7];
Now I added a label on top of the box:
SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:#"Arial"];
CGPoint *position = box.centerRect.origin;
position.y -= 55;
label.position = box.centerRect.origin;
label.text = #"a";
label.fontSize = 70;
label.fontColor = [SKColor blackColor];
Then I added the label onto the box and box onto the main scene:
[box addChild:label];
// self is the main scene.
[self addChild:box];
Then I call these code every 1 second. But the label only appears on some of them and the others are just the box, no label. I also added a fade out action for when the box was tapped. But when the fade out animation is going, the label on the box without the text starts to appear. Any ideas on why the label is not appearing on initializations? Thanks.
After a bits of researching, I found iOS 9's Sprite Kit handle default z position a bit different than iOS 8. So by adding this line will make all of the labels appear:
label.zPosition = box.zPosition + 1

User defined iOS interface

Introduction
I am writing an app where the interface on a screen can vary depending on user input on the previous screen. What I'm trying to do right now is load up x amount of sliders, where I get x from the previous screen. The real issue I'm having is won't each slider I use have to be named and defined? I've thought of a way to do this but I'm not entirely sure how to implement if you could advise me it would really be appreciated
Pseudo Code
-(void) loadInterface {
// Set up a frame
CGRect myFrame = CGRectMake(10.0f, 100.0f, 250.0f, 25.0f);
for (int i = 0; i < x; i++) {
[self newSlider]
// Retrieve info here about the slider and pass it to newSlider
}
}
-(void) newSlider (info about slider) {
// Here I need the code to load a slider
// If I use something like this:
// UISlider *slider1 = [[UISlider alloc] initWithFrame:myFrame];
// The next time this method is loaded the slider will be overwritten right?
}
You could use an NSMutableDictionary to keep track of all your sliders, although maybe you do not need that at all.
In newSlider you create a slider and you should add it to some view (I assume self) through
[self addSubview:slider];
All sliders thus created will appear at the position where you created them and will not disappear unless you remove them from the view.
You could take this snippet as an example of how you can create a slider:
CGRect frame = CGRectMake(0.0, 0.0, 200.0, 10.0);
UISlider *slider = [[UISlider alloc] initWithFrame:frame];
[slider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
If you define correctly frame, you can make all of your sliders align nicely in your UI.
When a slider action is carried through by the user, the sliderAction: method is called, which has an argument referring to the slider:
- (void)sliderAction:(UISlider* slider) {
...
}
so you know exactly which slider was actioned and this is generally enough to handle the action.
In any case, as I said, if you want to keep track of all the sliders, add them to a NSDictionary at the moment of creation:
[self.sliderDict setObject:newSlider forKey:#"nth-slider-name"];
You don't need new variables for each slider, because the scope of the UISlider instance you use in your newSlider method is limited to that method. Meaning the variable will be free for use once the program exits the method.
You should also pass which slider it is to that method, this can then be used to position it and to give the view a tag for later use:
-(void) loadInterface {
// Set up a frame
CGRect myFrame = CGRectMake(10.0f, 100.0f, 250.0f, 25.0f);
for (int i = 0; i < x; i++) {
[self newSlider:i]
// Retrieve info here about the slider and pass it to newSlider
}
}
-(void) newSlider:(int)sliderNumber {
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(0, sliderNumber * 100, 320, 50)];
slider.tag = sliderNumber;
[self.view addSubview:slider];
}

Mask touch area of UIImageview

I am using a mask to remove unwanted parts of an image. This all works correctly using the code below.
I then attach a tap gesture event to the image. However, I want the tap event gesture to be applied the result of the masked image rather than the full size of the UIimage frame. Any suggestions on how to do this?
CALayer *mask = [CALayer layer];
mask.contents = (id)[[UIImage imageNamed:self.graphicMask] CGImage];
mask.frame = CGRectMake(0, 0, 1024, 768);
[self.customerImage setImage:[UIImage imageNamed:self.graphicOff]];
[[self.customerImage layer] setMask:mask];
self.customerImage.layer.masksToBounds = YES;
//add event listener
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(customerSelected)];
[self.customerImage addGestureRecognizer:tap];
Update - My solution
In the end I decided not to use a mask and instead check the pixel colour of the touched point (as suggested by the correct answer). I used code from another question https://stackoverflow.com/a/3763313/196361.
I added a method to the view controller that is called whenever the view is touched.
- (void)touchesBegan:(NSSet*)touches
{
//check the colour of a touched point on the customer image
CGPoint p = [(UITouch*)[touches anyObject] locationInView:self.customerImage];
UIImage *cusUIImg = self.customerImage.image;
unsigned char pixel[1] = {0};
CGContextRef context = CGBitmapContextCreate(pixel,1, 1, 8, 1, NULL, kCGImageAlphaOnly);
UIGraphicsPushContext(context);
[cusUIImg drawAtPoint:CGPointMake(-p.x, -p.y)];
UIGraphicsPopContext();
CGContextRelease(context);
CGFloat alpha = pixel[0]/255.0;
//trigger click event for this customer if not already selected
if(alpha == 1.000000)
[self customerSelected];
}
If your mask is fairly rectangular then the easiest way would be to add transparent UIView on top with frame matching the masked region. Then you would add UITapGestureRecognizer directly to the invisible view.
EDIT
If you want your taps to be accepted based on the mask exactly then you can read the pixel color of mask in the tap location and check against your threshold.

How to show several subviews?

I'm having what I think is a bit of a newbie problem...
I've made a dynamic scroll view where images and labels are programmatically added as subviews. Problem is that only the last one I'm adding as a subview is showing.
Also when I read about "addSubview:" it says "Adds a view to the end of the receiver’s list of subviews." Does this mean only the last added subview is supposed show? In that case how do I make both visible?
Thanks in advance,
Tom
CODE:
for(int i = 0; i < [famorableArray count]; i++){
UIButton *famorableButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[famorableButton setFrame:CGRectMake(0.0f, 5.0f, 57.0f, 57.0f)];
[famorableButton setImage:personLogo forState:UIControlStateNormal];
NSString *famString = [NSString stringWithFormat:#"%#", [[[famorableArray objectAtIndex:i] substringFromIndex:8] capitalizedString]];
NSLog(#"%#", famString);
UILabel *famLabel = [[UILabel alloc] initWithFrame:CGRectZero];
famLabel.text = famString;
NSLog(#"Test2 %#", famLabel.text);
// Move the buttons position in the x-demension (horizontal).
CGRect btnRect = famorableButton.frame;
btnRect.origin.x = totalButtonWidth;
[famorableButton setFrame:btnRect];
CGRect labelRect = famLabel.frame;
labelRect.origin.x = totalButtonWidth + 28.5f;
[famLabel setFrame:btnRect];
// Add the button to the scrollview
[famScroll addSubview:famLabel];
[famScroll addSubview:famorableButton];
// Add the width of the button to the total width.
totalButtonWidth += famorableButton.frame.size.width + 30;
}
[famScroll setContentSize:CGSizeMake(totalButtonWidth, 79.0f)];
For each added subview you have to set the frame property. The frame is the view's position in the superview. "Adding to the list of subviews" does not imply any automatic layout. So I assume that all your subviews are visible but overlap.
My bad... It was a fail editing after copy paste. Accidentally used btnRect as a frame for the label too. Thanks for your effort anyways #Martin :)

UIImageView blurs image

I have a really weird problem with UIImageView. I have an image (an RGB png) 45x45 pixels which I add to the view. I can see that image is blurred after added to the view. Here is the same image in the simulator (left) and in Xcode (right):
(source: partywithvika.com)
(source: partywithvika.com)
I have custom UIImageView class with this initWithImage code:
- (id) initWithImage:(UIImage*) image {
self = [super initWithImage:image];
self.frame = CGRectMake(0, 0, 45, 45);
self.contentMode = UIViewContentModeScaleAspectFit;
self.quantity = 1;
if (self) {
self.label = [[UITextField alloc]initWithFrame:CGRectMake(0,40,45,25)];
self.label.font = [UIFont systemFontOfSize:16];
self.label.borderStyle = UITextBorderStyleNone;
self.label.enabled = TRUE;
self.label.userInteractionEnabled = TRUE;
self.label.delegate = self;
self.label.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.label.textAlignment = UITextAlignmentCenter;
}
self.userInteractionEnabled = TRUE;
// Prepare 3 buttons: count up, count down, and delete
self.deleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.deleteButton.hidden = NO;
self.deleteButton.userInteractionEnabled = YES;
self.deleteButton.titleLabel.font = [UIFont systemFontOfSize:20];
self.deleteButton.titleLabel.textColor = [UIColor redColor];
[self.deleteButton setTitle:#"X" forState:UIControlStateNormal];
[self.deleteButton addTarget:self action:#selector(deleteIcon:) forControlEvents:UIControlEventTouchUpInside];
self.upCountButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.upCountButton.hidden = NO;
self.upCountButton.userInteractionEnabled = YES;
[self.upCountButton setTitle:#"+" forState:UIControlStateNormal];
[self.upCountButton addTarget:self action:#selector(addQuantity:) forControlEvents:UIControlEventTouchUpInside];
self.downCountButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.downCountButton.hidden = YES;
self.downCountButton.userInteractionEnabled = YES;
[self.downCountButton setTitle:#"-" forState:UIControlStateNormal];
[self.downCountButton addTarget:self action:#selector(removeQuantity:) forControlEvents:UIControlEventTouchUpInside];
return self;
}
I create it like this:
UIImage *desertIcon = [UIImage imageNamed:#"desert.png"];
IconObj *desertIconView = [[IconObj alloc] initWithImage:desertIcon];
desertIconView.center = CGPointMake(265,VERTICAL_POINT_ICON);
desertIconView.type = [IconObj TYPE_DESERT];
[self.view addSubview:desertIconView];
[desertIconView release];
Why would the displayed image be so than the one stored in a file?
In general, everything you do on iPhone with UIKit should be pixel-aligned. Problems with pixel alignment usually manifest as blurriness (this is especially true with text and images). This is why when I find blurry view, I first check if I'm setting the center property. When you set center, the frame's x, y, height and width are adjusted around that center point... frequently resulting in fractional values.
You could try using CGRectIntegral on the frame as shown:
desertIconView.center = CGPointMake(265,VERTICAL_POINT_ICON);
desertIconView.frame = CGRectIntegral(desertIconView.frame);
This may work, but if it doesn't, try setting the frame manually, without using center property to ensure that no values are fractional.
Edit: Whoops! Didn't notice that the OP had answered his own question... I'll leave this up for informational reasons anyway.
I had this problem and it was driving me nuts. After some investigation it turned out that my image was smaller than the frame, and hence the scaling up blurred it. Once I had put in higher resolution images the problem is gone.
Make sure your image size is greater than your UIImageView frame size.
Your IconObj is 45 pixels wide. You move your IconObj center to 265 which makes its frame to (242.5, VERTICAL_POINT_ICON - 25*0.5, 45, 25). Image will always be blur if some of frame parameter is not integer.
Solution, calculate the frame parameter yourself (don't use center). And always make it integer (cast to NSInteger, use ceil, floor, or round).
desertIconView.frame = CGRectMake((NSInteger)(265 - 45*0.5),
(NSInteger)(VERTICAL_POINT_ICON - 25*0.5),
45, 25);
What I ended up doing is loading bigger picture into 45x45 UIImageView, of course with
contentMode = UIViewContentModeScaleAspectFit;
I just had the same problem. First I thought wtf do I need glasses?
Then I realized it´s just not possible to center an image (or label) when you give it an odd number. You need to resize your image to e.g. 46*46 if you want to center it and stay sharp.
Or you can leave it at 45*45 but then you need to decide whether you want your image to be mal-centered 1 pixel to the right, or 1 pixel to the left. (or leave it blurry).
Try to align your IconObj view at screen pixels. If it's really 45x45 then you should set the center to something like CGPointMake(x + 0.5f, y + 0.5f).
You should also double check image size in pixels (e.g in Preview.app Command-I).
complete source for the offset problem:
contentImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
UIImage *cImage = [UIImage imageNamed:image];
self.contentImage.image = cImage;
[self.contentImage setFrame:CGRectMake(0, 0, cImage.size.width, cImage.size.height)];
CGFloat offset = 0.0f;
if ( ( (int)cImage.size.width % 2 ) && ( (int)cImage.size.height % 2 ) ) {
offset = 0.5f;
}
[self.contentImage setCenter:CGPointMake(256.0f + offset, 256.0f + offset)];