I guess I'm missing something obvious, but I simply can't figure out how to obtain the label of a CCMenuItemFont.
Background
I'm building a simple hangman game for the iPad. For entering the next guess, I've added 26 buttons to the UI (one for each letter of the alphabet) and wired them all to the same event handler.
Now, inside the event handler, I'd like to obtain the label of the button to update the current guess, but CCMenuItemFont apparently doesn't respond to text or label.
Problem
So - what method can I use to obtain the label of a CCMenuItem?
Code
Code for creating the buttons:
-(void)addButtons {
NSArray* charArray = [NSArray arrayWithObjects:
#"A",#"B",#"C",#"D",#"E",#"F",#"G",#"H",#"I",#"J",#"K",
#"L",#"M",#"N",#"O",#"P",#"Q",#"R",
#"S",#"T",#"U",#"V",#"W",#"X",#"Y",#"Z", nil];
[CCMenuItemFont setFontName:#"Marker Felt"];
[CCMenuItemFont setFontSize:45];
NSMutableArray* buttonArray = [NSMutableArray array];
for (unsigned int i=0; i < [charArray count]; ++i) {
CCMenuItemLabel* buttonMenuItem = [CCMenuItemFont
itemWithString:(NSString*)[charArray objectAtIndex:i]
target:self selector:#selector(buttonTapped:)];
buttonMenuItem.color = ccBLACK;
buttonMenuItem.position = ccp(60 + (i/13)*40, 600 - (i%13)*40);
[buttonArray addObject:buttonMenuItem];
}
CCMenu *buttonMenu = [CCMenu menuWithArray:buttonArray];
buttonMenu.position = CGPointZero;
[self addChild:buttonMenu];
}
And the event handler:
- (void)buttonTapped:(id)sender {
// Get a reference to the button that was tapped
CCMenuItemFont *button = (CCMenuItemFont *)sender;
[_guess addObject:[button text]]; // this throws an exception because text is the wrong method
[self paintCurrentGuess];
}
You're adding to your menu a CCMenuItemLabel, not a CCMenuItemFont (which actually extends the first one). In both cases, you need to access to the inner label containing the text.-
CCMenuItemLabel *button = (CCMenuItemLabel *)sender;
NSString *label = button.label.string;
Related
I've searched this issue for a solid amount of time, and I couldn't find an answer that solved my problem. If there are any similar questions that I missed and that have a working solution, please post a link to them.
Anyways...
I have a menu filled with buttons that, when clicked, opens up a UIWebView which opens a specific webpage. Everything works fine, but only after the second try. To be a little more clear, the first time I tap a button, the webpage gets stuck loading, but after you close it out and tap it again, the pages load up just fine.
Since I'm using multiple buttons, I decided to put them all in an IBOutlet Collection:
#property (nonatomic, retain) IBOutletCollection(UIButton) NSArray *btnArray;
I then set up an array that would hold all the buttons from the collection, set up a view controller for our webview class (we have a separate class for webviews, which has not caused me any problems in the past) and the webview's navigation controller, and finally I used a for loop to add all the buttons the array.
NSMutableArray *theButtons = [[NSMutableArray alloc] init];
webVC = [[WebviewViewController alloc] init];
webNav = [[UINavigationController alloc] initWithRootViewController:webVC];
for(int i = 0; i < [btnArray count]; i++)
{
UIButton *btn = btnArray[i];
btn.tag = i;
[theButtons addObject:btn];
[theButtons[i] addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
I also have set up an array with the end links of all the pages I need to go to, which will be appended to a url:
pages = #[#"exOne.php", #"exTwo.php", #"exThree.php", #"exFour.php", #"exFive.php", #"exSix.php"];
Note: all the above code in -viewDidLoad
Here is the method that handles what happens when a button is clicked:
-(void)buttonPressed : (id) sender
{
UIButton *clicked = (UIButton *) sender;
NSMutableString *url = [NSMutableString stringWithString:#"http://www.example.org/examples/"];
for(int i = 0; i < [btnArray count]; i++)
{
if(i == clicked.tag)
{
[url appendString:pages[i]];
[webVC loadURL:url];
webNav.navigationBar.translucent = NO;
webNav.navigationBar.topItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(backPressed)];
[self.navigationController pushViewController:webVC animated:YES];
}
}
}
If it wasn't clear from the method call
[webVC loadURL:url];
just sends the link to our webviewViewController class and loads the link.
Any ideas on why the webpages only load after the second attempt?
Your WebviewViewController has probably not been loaded when you call loadURL the first time. If the view controller is not loaded it's subviews and hence it's embedded UIWebView are not available. When you push the controller it gets loaded and the second time loadURL will work.
I have programmatically generated UIButtons that all share the same selector method. When the method runs I would like the method to know which button was pressed and then be able to load a corresponding UIViewController.
-(void)buildButtons
{
for( int i = 0; i < 5; i++ ) {
UIButton* aButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[aButton setTag:i];
[aButton addTarget:self action:#selector(buttonClicked:)forControlEvents:UIControlEventTouchUpInside];
[aView addSubview:aButton];
}
Then:
- (void)buttonClicked:(UIButton*)button
{
NSLog(#"Button %ld clicked.", (long int)[button tag]);
// code here that picks the correct viewController to push to...
// for example tag 1 would create an instance of vcTwo.m and would then be pushed to the navigationController and be displayed on screen
}
say I have three UIViewController classes (vcOne.m, vcTwo.m, vcThree.m) and I want it so that when the button is pressed 'buttonClicked' is run and the code picks the corresponding viewController to push to. I don't want to use a series of if statements as there may be dozens/hundreds of viewControllers in the end. Would I have to instantiate all of the viewControllers and put them in an array? Is there a better way?
Are you using storyboards? so you can chose a segue according to the button tag:
int i = (int)[button tag];
[self performSegueWithIdentifier:[NSString stringWithFormat:#"Segue%d", i] sender:self];
or:
UIViewController *viewController= [controller.storyboard instantiateViewControllerWithIdentifier:NSString stringWithFormat:#"ViewControllerNumber%d", i];
In the end I went with this:
- (void) buttonClicked:(id)sender
{
NSLog(#"Button tag = %li", (long)[sender tag]);
FormularyVC *formularyVCInstance = [FormularyVC alloc];
ProceduresVC *proceduresVCInstance = [ProceduresVC alloc];
VetMedVC *vetMedVCInstance = [VetMedVC alloc];
NSArray *vcArray = [NSArray arrayWithObjects:formularyVCInstance, proceduresVCInstance, vetMedVCInstance, nil];
UIViewController *vcToLoad = [vcArray objectAtIndex:(int)[sender tag]];
vcToLoad.view.backgroundColor = [UIColor whiteColor];
[self.navigationController pushViewController:vcToLoad animated:NO];
}
I created an array of the ViewControllers I wanted to be able to load based on which button was pressed. When the button is pressed the method is run and the tag is taken as a parameter. This tag is used to find the ViewController needed by checking its location on the array's index.
I have 6 UIButtons set up in my UIView, all in the exact same location. What I want to do, is to swipe from left to right or right to left, in order to go through the buttons.
I have the UIGestures all set up and working on the view, I am just not sure the best way to go about cycling through these UIButtons.
I was thinking that it could be simple enough to tag each UIButton and HIDE all but one, but am not sure about the best way to loop through these.
Just put them in an NSMutableArray and whatever button is at index 0 is the visible one, as they swipe you'd remove the button at index 0, set it to hidden = YES and add it to the end of the array then set the button at index 0's hidden = NO.
Assuming you're using ARC, inside your class' implementation (.m) file:
#interface MyFancyButtonClass () {
NSMutableArray *_swipeButtons;
}
inside your viewDidLoad:
_swipeButtons = [NSMutableArray arrayWithObjects:buttonOne, buttonTwo, buttonThree, buttonFour, nil];
buttonOne.hidden = NO;
buttonTwo.hidden = buttonThree.hidden = buttonFour.hidden = YES;
inside your gestureRecognizer:
UIButton *currentVisibleButton = [_swipeButtons firstObject];
UIButton *nextVisibleButton = [_swipeButtons objectAtIndex:1];
[_swipeButtons removeObject:currentVisibleButton];
[_swipeButtons addObject:currentVisibleButton];
currentVisibleButton.hidden = YES;
nextVisibleButton.hidden = NO;
Create a NSMutableArray like this:
NSMutableArray * buttons = [[NSMutableArray alloc] init];
[buttons addObject:button1];
[buttons addObject:button2];
[buttons addObject:button3];
[buttons addObject:button4];
[buttons addObject:button5];
Save this array to a property like this
self.buttons = buttons;
Store the currentButton Like this:
int currentButton = 0;
Get the current button like this:
UIButton * currentSelectedButton = buttons[currentButton];
Cycle through the buttons like this:
UIButton * currentSelectedButton = buttons[currentButton];
currentSelectedButton.hidden = YES;
currentButton++;
if (currentButton >= self.buttons.count)
currentButton = 0;
currentSelectedButton = buttons[currentButton];
currentSelectedButton.hidden = NO;
I'm working on an iPhone game and currently the game uses many UIImageViews to show space ships, bullets and other things. The game probably has 30-40 different subviews in the view controller and I've decided that I should probably programmatically add subviews when they are needed rather than starting the game with them in the hidden state and making them unhidden when needed (for performance issues) so here are my questions:
(1) will there be a significant performance gain from adding subviews when needed?
(2) how do I release a subview when ARC is enabled? Im trying to add the views (based on type) into an NSMutableArray when its created to hold it, and removing it from the array when I'm done using it, does removing it from the array deallocate that memory?
heres the code (theres an NSTimer that runs the movement method every 0.03 seconds, and the get and set methods are on an "instance variable" NSMutableArray);
-(void)movement {
if (shootButton3.isHighlighted) {
NSMutableArray *array = [self getBulletArray];
[self setarray:array];
}
[self shootBullet: [self getArray]];
}
-(NSMutableArray *)getBulletArray {
UIImageView *bullet = [[UIImageView alloc] initWithFrame:CGRectMake(ship.center.x + 20, ship.center.y, 15, 3)];
bullet.image = [UIImage imageNamed:#"bullet2.png"];
bullet.hidden = NO;
[self.view addSubview:bullet];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:bullet];
return array;
}
-(void)shootBullet: (NSMutableArray *)bulletArray {
for (int i = 0; i < [bulletArray count]; i++) {
UIImageView *bullet = [bulletArray firstObject];
bullet.center = CGPointMake(bullet.center.x + 4, bullet.center.y);
if (bullet.center.x > 500) {
bullet.hidden = YES;
[bulletArray removeObjectAtIndex:0];
}
}
}
-(void)setarray:(NSMutableArray *) array {
bulletArray = array;
}
-(NSMutableArray *)getArray {
return bulletArray;
}
To answer your two questions:
Question 1: Yes, there will be a performance increase for sure, regardless of how large the UIImageViews are.
Question 2: Simply set the imageView to nil: self.imageView = nil;
I understand (I think) why this isn't working, but I don't know how to get it to work.
I have a method I call in viewWillAppear like so (I've simplified the code for the sake of this question):
- (void)viewWillAppear:(BOOL)animated
{
[self displayHour:0];
}
I then have a little for loop that builds some buttons in viewDidLoad:
NSUInteger b = 0;
for (UIButton *hourButton in hourButtons) {
[hourButton setTag:b];
[hourButton setTitle:[NSString stringWithFormat:#"%lu", (unsigned long)b] forState:UIControlStateNormal];
[hourButton addTarget:self action:#selector(showHour:) forControlEvents:UIControlEventTouchUpInside];
b++;
}
Then for the action I have this:
- (IBAction)showHour:(id)sender
{
// Logs 0, 1, etc. depending on which button is tapped
NSLog(#"Button tag is: %lu", (long int)[sender tag]);
// This currently throws this error:
// No visible #interface for 'UIView' declares the selector 'displayHour:'
// What I want to do is be able to call this method on
// the main view and pass along the button tag of the
// button that was tapped.
[mainView displayHour:(long int)[sender tag]];
}
THE QUESTION is how do I call displayHour to the main view from the showHour: action?
If you need it, the displayHour method looks like this (simplified, basically updates some labels and image views on the main view):
- (void)displayHour:(NSUInteger)i
{
// The temperature
hourTemp = [[hourly objectAtIndex:i] valueForKeyPath:#"temperature"];
// The icon
hourIcon = [[hourly objectAtIndex:i] valueForKeyPath:#"icon"];
// The precipitation
hourPrecip = [[hourly objectAtIndex:i] valueForKeyPath:#"precipProbability"];
// SET DISPLAY
[hourTempField setText:hourTemp];
[hourIconFrame setImage:hourIcon];
[hourPrecipField setText:hourPrecip];
}
Thanks!
Instead of
[mainView displayHour:(long int)[sender tag]];
do:
[self displayHour:(long int)[sender tag]];