I'm working on a nice app as per usual and then this happens! You spin the wheel of fortune in my app, and when the spinning animation is finished, the app is supposed to randomize a number, and display the card matched as the number index.
The cards are stored in a NSMutableArray, working perfectly when called the first time. Eg. when the app randomizes number "1" card number 1 is called and displayed without problem!
But once the randomizer hits "1" again, the app crashes on the line specified below.
Here's the code, PLEASE help me :)
NSNumber *cardNumber;
cardNumber = [NSNumber numberWithUnsignedInt:arc4random()%[appDelegate.cardArray count]];
NSLog(#"%#", cardNumber);
SpinCard *selectedCard = [appDelegate.cardArray objectAtIndex:[cardNumber doubleValue]];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
[snurrKnapp setAlpha:100];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:cardView cache:YES];
[UIView commitAnimations];
[snurrKnapp setEnabled:YES];
if(![cardTitle.text isEqualToString:selectedCard.cardTitle]) { //Crashes here
[cardTitle setText:selectedCard.cardTitle]; //If I remove the if-case it crashes here
[cardContent setText:selectedCard.cardContent];
}
[selectedCard release];
Here's the error I get:
2011-04-21 23:10:54.296 Snurra Flaskan[947:707] 0
2011-04-21 23:10:58.794 Snurra Flaskan[947:707] 2
2011-04-21 23:11:02.691 Snurra Flaskan[947:707] 1
2011-04-21 23:11:08.977 Snurra Flaskan[947:707] 2
warning: Unable to read symbols for /Developer/Platforms/iPhoneOS.platform/DeviceSupport/4.3.1 (8G4)/Symbols/Developer/usr/lib/libXcodeDebuggerSupport.dylib (file not found).
(gdb)
I'm very new in Obj-C, but here is what I think is happening.
You should not call [selectedCard release];
Because objectAtIndex does not allocate a new SpinCard for selectedCard. You should not release it. That explains the behaviour for the second use of that SpinCard: you can't use it, it has already been released.
Related
I have subclassed UIAlertView and inside which I am showing a textField which takes the input. When user clicks on textField the keyboard shows up and UIAlertView moves up to adjust for keyboard. But when I do [textField becomeFirstResponder] in didPresentAlertView delegate method of UIAlertView, the alertView doesn't moves up to adjust for keyboard. Instead the UIAlertView gets hidden behind the keyboard.
PS - I know that Apple says that UIAlertView should not be subclassed and to be used as it is, but I am subclassing UIAlertView because I want to redesign the Apple's default UI elements in it.
You should really not do something against Apple's recommendation.
Reasons
You can face unexpected issues like the one you are facing.
Your code can break for future iOS releases as you are violating recommendations.
Redesigning Apple's standard controls is in violation of HIG guidelines. As a result, there is a chance that your app can get rejected. Create your own instead, by using UIView subclass.
As an alternative, Apple has made provision in the UIAlertView for this requirement. You don't need to add a textfield to the alert view, instead, use the UIAlertView property alertViewStyle. It accepts values defined in the enum UIAlertViewStyle
typedef NS_ENUM(NSInteger, UIAlertViewStyle) {
UIAlertViewStyleDefault = 0,
UIAlertViewStyleSecureTextInput, // Secure text input
UIAlertViewStylePlainTextInput, // Plain text input
UIAlertViewStyleLoginAndPasswordInput // Two text fields, one for username and other for password
};
Example, lets assume a use case that you want to accept password from the user. The code to achieve this is as below.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Please enter password"
message:nil
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Continue", nil];
[alert setAlertViewStyle:UIAlertViewStyleSecureTextInput];
[alert show];
To validate the input, lets say password entered must be minimum 6 characters, implement this delegate method,
- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView
{
NSString *inputText = [[alertView textFieldAtIndex:0] text];
if( [inputText length] >= 6 )
{
return YES;
}
else
{
return NO;
}
}
To get the user input
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Login"])
{
UITextField *password = [alertView textFieldAtIndex:0];
NSLog(#"Password: %#", password.text);
}
}
To re-iterate,
UIAlertView has a private view hierarchy and it is recommended to use it as-is without modification. If you use it against recommendation you will get unexpected results.
From Apple docs
The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
This is standard technique used even in iOS default apps (Ex: Entering Wi-Fi password, etc.), hence using this will ensure you don't face issues like the one you mention.
Hope that helps!
I do like this to upthe screen to show the textfiled. :-) Hope this help you.
- (void) textFieldDidBeginEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
self.view.frame = CGRectMake(( self.view.frame.origin.x), (self.view.frame.origin.y-50 ), self.view.frame.size.width, self.view.frame.size.height);
[UIView commitAnimations];
}
- (void) textFieldDidEndEditing:(UITextField *)textField {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.5];
[UIView setAnimationBeginsFromCurrentState:YES];
self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y+50 , self.view.frame.size.width, self.view.frame.size.height);
[UIView commitAnimations];
}
In my UITextField,when I type # ,I am able to show a pop up containing an array values.
But now my client need to do some modifications.If a user type #, popup should not come,but when he type any letter after #,all the friends name starting with that letter should come in a popup.
Eg:- if user typed #p - pop up will come with all the friends name starting with letter P.
How to do this,I have tried something but could not make it happen
(I am getting friends list in an array while loading the view)
Now I am using
- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if([string isEqualToString:#"#"]) {
s=1;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[frndsView setCenter:CGPointMake(frndsView.center.x, frndsView.center.y-310)];
[UIView commitAnimations];
[commentField resignFirstResponder];
}
return YES;
}
You mean Auto Complete. Check this tutorial.
My action method:
- (IBAction)buttonPressed {
// animate picker to random row (picker is a UIPickerView)
int row = random() % [self.column1 count];
[picker selectRow:row inComponent:0 animated:YES];
[picker reloadComponent:0];
// display new selected row content
int selectedRow = [picker selectedRowInComponent:0];
NSString *selectedItem = [self.column1 objectAtIndex:selectedRow];
myLabel.text = selectedItem; // UILabel under the picker
}
However
myLabel.text = selectedItem;
gets called before the animation has completed and so it doesn't display the new value.
I found the thread How to get callback from UIPickerView when the selectRow animation is done?, but the answer uses a beginAnimations/commitAnimation block - about which apple says: "Use of this method is discouraged in iOS 4.0 and later. You should use the block-based animation methods to specify your animations instead."
How would I use block-based animation to accomplish this?
It looks like Apple needs to update their UIPickerView API to support a block based completion handler. Until then, just wait the estimated time of the animation.
[picker selectRow:0 inComponent:0 animated:YES];
[self performSelector:#selector(setMyLabel) // setMyLabel - my function
withObject:nil
afterDelay:0.4];
Have you tried something like this:
[UIView animateWithDuration:2.0
delay:0.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
// the animation code
[myPickerView selectRow:0 inComponent:0 animated:YES];
}
completion:^(BOOL finished) {
[self performSelector:#selector(setMyLabel)];
}
];
Or you could just set the label directly in the completion block, if that's the only thing you want to do after the animation completes.
EDIT:
The code above is the same as the code in the other thread you've mentioned, but using animation blocks instead. I actually didn't know if the method selectRow:inComponent:animated: will run on the same animation block thread or it will have its own thread. Since it didn't work, that means it runs on its own thread so the code I wrote won't work.
There is one way around to solve this, which is by blocking the main thread for the time needed for the animation to complete. You can call [NSThread sleepForTimeInterval:1.0] after your call to selectRow:inComponent:animated:, and you can adjust the interval until you reach a suitable value. Note that blocking the main thread is highly discouraged by Apple. Personally I would block it only if I couldn't think of another way to achieve what I'm trying to do and if the blocking time is smaller than 1.
I wonder if anyone can help with the following. I have integrated both iAds and AdMob into my app. However a user reported that the app crashes on the iPod Touch. Using Instruments in xCode I have managed to identify that something called "GOOGLE_SHUFFLE_RVS_User_waylonis_Code_afma1_googlmac_iPhone_GoogleAds_Signals_Protected_build_GoogleAdsSignals_build_Release_iphoneos_Google" is causing a memory leak of about 500 bytes everytime it is called.My ad refresh rate is set at 20 seconds so this happens every 20 seconds.
My code is as follows.
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible)
{
[UIView beginAnimations:#"animateAdBannerOff" context:NULL];
// banner is visible and we move it out of the screen, due to connection issue
banner.frame = CGRectOffset(banner.frame, 0, -90);
[UIView commitAnimations];
self.bannerIsVisible = NO;
}
[self loadAdMobAd];
}
-(void)loadAdMobAd {
if (!bannerView_) {
CGRect adSize = CGRectMake (0,40,0,0);
adSize.size = GAD_SIZE_320x50;
bannerView_ = [[GADBannerView alloc] initWithFrame:adSize];
bannerView_.rootViewController = self;
bannerView_.adUnitID = MY_BANNER_UNIT_ID;
bannerView_.rootViewController = self;
[self.view addSubview:bannerView_];
// Initiate a generic request to load it with an ad.
[bannerView_ loadRequest:[GADRequest request]];
}
}
The idea is that if an iAd is not available an AdMob ad is loaded instead.
Is there anything wrong with my code that could be causing the leak ?
Many thanks,
Martin
Apparently the GOOGLE_SHUFFLE_RVS memory leak is a known issue. According to the Google Group (http://groups.google.com/group/google-admob-ads-sdk/browse_thread/thread/2631fcb87d909bfa/edafd2a4ac175f47?lnk=gst&q=memory+leak#edafd2a4ac175f47), "it's a known glitch, and it'll be fixed in the next release" (from a comment posted on March 31). They also say it's fixed internally but not released yet.
I was very surprised that AdMob/Google didn't give higher priority to something as significant as an ad banner that leaks memory every time an ad loads. I guess everyone is just using the memory leaking version for now. :-o
Joe
You're alloc'ing bannerView_, adding it to the view, but not releasing it.
Try adding [bannerView_ release]; after the loadRequest line.
I have a MasterViewController.h.m.xib (UIViewController) that is opening a TestDummy.h.m.xib (UIViewController) in the following way:
TestDummy *controller = [[TestDummy alloc] initWithNibName:#"TestDummy" bundle:nil];
[scrollView addSubview:controller.view];
I have two buttons in TestDummy: (Open), (Close) and one label: (windowDepth).
I'm trying to create a second instance TestDummy that is open by the first TestDummy. Then allow multiple TestDummy (UIViewController) to open to N depth and allow the close button to take them back to zero depth. Here's what i have for my Open button.
-(IBAction) btnOpen_Clicked{
TestDummy *newController = [[TestDummy alloc] initWithNibName:#"TestDummy" bundle:nil];
newController.isNotRoot = YES;
newController.windowDepth = self.windowDepth + 1;
//do stuff...
childDummy = newController;
// start the animated transition
[UIView beginAnimations:#"page transition" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
//insert your new subview
[self.view addSubview:newController.view];
// commit the transition animation
[UIView commitAnimations];
[newController release];
}
When i do this i get an error in the debug console.
2010-10-07 00:59:12.549 OrionClient[5821:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType btnOpen_Clicked]: unrecognized selector sent to instance 0x6a339a0'
Must be a memory management issue but i can't figure it out.
Thanks in advance.
'NSInvalidArgumentException', reason:
'-[__NSCFType btnOpen_Clicked]:
unrecognized selector sent to instance
0x6a339a0'
It means you are trying to call a non existing method on that instance. How have you defined the btnOpen_Clicked selector? I'm guessing it should look more like, but really need to see how you defined the selector.
-(IBAction) btnOpen_Clicked:(id)sender
It means that the application can't find your method btnOpen_Clicked.
First rename your method with :
-(IBAction) btnOpen_Clicked:(id)sender
Then make sure this method specification is in the .h file
And in InterfaceBuilder with your TestDummy.xib, make also sure that the link between the button and this method is correctly done with for example TouchUpInside event.
solved it removed the last line [newController release]; need to figure out where to actually call this correctly though.