Push view programmatically from UIAlertView button? - ios

I would open a UIViewController from a UIAlertView button but it doesn't work.
When user terminate a game I show a UIAlertView with some text and two buttons: "Ok" to dismiss alert and "Score" to open the score page.
This is the actual UIAlertView:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"YOU WIN!"
message:[NSString stringWithFormat:#"You win a game in %d seconds!",seconds]
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:#"Score", nil];
[alert show];
and this is the code for push the view:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if([title isEqualToString:#"Score"]){
ScoreViewController *score = [[ScoreViewController alloc]init];
[[self navigationController] pushViewController:score animated:YES];
}
}
What I obtain is a completely black screen. Can someone help me? I can't figure it out what I'm doing wrong.
PS: ScoreViewController has a segue from the root view but of course I can't create a new one from storyboard because I want to perform segue programmatically, from the alert view button.
Hope I've been clear, any help will be very appreciate!

It looks like you're instantiating an instance of ScoreViewController and pushing onto your navigation controller. While this is a legitimate way of presenting another controller, it's different from performing a Storyboard segue.
First, obviously, you'll have to make sure you have a segue connecting your view controllers in the Storyboard. It sounds like you've made it that far, so next you'll want to make sure the segue has an identifier. Select the segue and set its identifier in the Attributes inspector. Then perform the segue in code:
[self performSegueWithIdentifier:#"YourIdentifier" sender:self];

Related

tabBarController delegate didn't work

I use below delegate method to set the tabBarController not to pop to another sub view controller by setting the return value to NO,
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
UIViewController *vc =[((UINavigationController *)viewController).viewControllers objectAtIndex:0];
if ([vc isKindOfClass:NSClassFromString(#"LYAppCategoryViewController")]) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"" message:#"" delegate:self cancelButtonTitle:#"" otherButtonTitles:#"", nil];
[alert show];
return NO;
}
else {
return YES;
}
}
but after "NO" was actually returned,the tabBarController still pop to another sub view controller. So it's kinda puzzling,am I getting it wrong, the scenario of using it?
quotes from Apple API "YES if the view controller’s tab should be selected or NO if the current tab should remain active."
thanks a lot for your kind help.
I still didn't get the reason why this happened,but I accomplished my target by adding codes like this:
[tabBarController setSelectedViewController:[[tabBarController viewControllers] objectAtIndex:0]];
[tabBarController setSelectedIndex:0];
tabBarController will not pop to another view controller if "forcing" to pop to the first one.

How to have a UIBarButton execute a custom function?

I'm hoping someone can help me figure this out...I'm a beginner Xcode / Objective-C programmer. I'm working on a app that is a continuation of last semester.
1: So I created a button and I need it to execute this custom function:
- (void)cancelTapped {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Notification" message:#"Do you want to delete everything and go back to product selection?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes"];
[alert setTag:1];
[alert show];
}
How/where do I put this function? Is it in the button properties? Or would I write this in a custom class/controller and link it to it?
2: How do I get it to listen for the alert to return on: - alertView:didDismissWithButtonIndex:
3: From there, how would I would write the logic to hide the page and pop the view?
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (alertView.tag == 1 && buttonIndex == 1) {
// Delete data and return to lobby
[self.navigationController popViewControllerAnimated:YES];
}
}
You will need a custom UIViewController to house the logic for your button and alert view interactions. I am assuming you know how to do that.
Once done, assuming you have a reference to your button property in your view controller, you can programatically add a target to your button and pass the selector cancelTapped as a parameter:
[myButton addTarget:self action:#selector(cancelTapped) forControlEvents:UIControlEventTouchUpInside];
Alternatively you could control-drag from the button in your Storyboard to the header file of your custom UIViewController, and define an IBAction. That will create an empty cancelTapped method in your implementation which you could then add your logic in.
As for listening on UIAlertView messages, you will need to make your custom UIViewController a delegate of the UIAlertView by passing "self" as the delegate in the following statement:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Notification" message:#"My Message" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes"];
Your CustomViewController should also be declared as a UIAlertViewDelegate.
CustomViewController.h
#interface CustomViewController : UIViewController<UIAlertViewDelegate>
#end
Hope this helps!
By the using VIewWillDisappear method to detect the press of The back button of NavigationItem:
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// Navigation button was pressed. Do some stuff
[self cancelTapped];
}
[super viewWillDisappear:animated];
}
- (void)cancelTapped {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Notification" message:#"Do you want to delete everything and go back to product selection?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes"];
[alert setTag:1];
[alert show];
}
For More info && also Custom UIBArButtonItem Check Here

How should I present a Modal VIew Controller from TabBarController

I have an app whose initial scene is a tab bar controller with 3 tabs. I created a uitabbarcontroller class and set it to that scene (MainTabViewController).
In that class I call presentLogin from the viewDidAppear method and that method reads:
- (void)presentLogin{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if (![prefs stringForKey:#"storedUser"] && ![prefs stringForKey:#"storedPass"]) {
NSLog(#"No user prefs stored");
// BUT WAIT, before all this, lets pop up a view controller for user registration
UIStoryboard* sb = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
ModalViewController *popupController = [sb instantiateViewControllerWithIdentifier:#"ModalViewController"];
[self presentViewController:popupController animated:YES completion:nil];
} else {
NSString *storedUser = [NSString stringWithFormat:#"User:%#",[prefs stringForKey:#"storedUser"]];
NSString *storedPass = [NSString stringWithFormat:#"User:%#",[prefs stringForKey:#"storedPass"]];
UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:storedUser
message:storedPass
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
[internetAlert show];
}
}
But the modalVC isnt showing for some reason. I get this crash log:
Attempting to begin a modal transition from <MainTabViewController: 0xa55d0d0> to <ModalViewController: 0x15e2b5e0> while a transition is already in progress. Wait for viewDidAppear/viewDidDisappear to know the current transition has completed
I believe you get this error because the tab bar controller is putting the view of the controller in its first tab on screen at the same time you're presenting the modal controller. Instead of presenting it from the tab bar controller, present it in the viewDidAppear method of the controller in the first tab. Call it with no animation to see the modal view controller without seeing the firs tab controller.
Try to add a tiny delay like below:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(presentLogin) withObject:nil afterDelay:0.1];
}
The view of the tabbarcontroller contains the viewHierarchies of the viewControllers that the tab bar itself owns. Maybe something is of because of that. Try to see of you still get the error if you only have one viewcontroller set to the tabbar.

Duplicate UIViewControllers on UINavigationController stack after application wakes

I just learnt IOS programming and am writing my first iPhone app.
My app provides info on an MKMapView, the view controller of which is the root view controller of a UINavigationController. If the mobile signal is bad i use mapViewDidFailLoadingMap:withError: to make the app push one of two different view controllers onto the navigation controller stack depending on what the user is doing. Code follows:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
{
NSLog(#"mapViewDidFailLoadingMap: %#", [error localizedDescription]);
[aiView stopAnimating];
if (mapTypeInt == 0) {
NSString *message =
[NSString stringWithFormat:#"Your signal is not currently
strong enough to download a map. Switching to table view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Maps Unavailable"
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
if (currentMode == #"retracingSteps")
{
RetraceViewController *rvc =
[[RetraceViewController alloc] initWithNibName:#"RetraceViewController" bundle:nil];
[[self navigationController] pushViewController:rvc animated:YES];
}
else{
TripTableViewController *ttvc = [[TripTableViewController alloc] init];
[[self navigationController] pushViewController:ttvc animated:YES];
}
}
else{
[self setMapType:0];
NSString *message = [NSString stringWithFormat:
#"Your signal is not currently strong enough to download a satellite map.
Switching to Standard Map view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Can't Use Satellite Maps"
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}
}
Yesterday I tested the poor mobile signal in a rural valley and the correct second view controller was pushed onto the stack. What I then noticed when I locked the phone and rechecked the app after a few minutes was that on waking, the root view controller was displayed quickly followed by the view controller I expected. In effect what this did was to push an identical copy of the second view controller onto the stack. I discovered this when I had to tap the back button half a dozen times to get back to the root view controller.
What I would like the app to do on wake up is to immediately display the view controller that was live when the phone is locked rather than the root view controller. I do not know how to do this.
This is probably happened because even you lock the screen your app still receiving locations update (location services can run code in a real background mode), also when you push a view controller the previous one in the stack still exist and still receiving location update and doing everything has been designed to do, so even you push another controller, if something happen that for the implemented logic has to push another controller, this view controller has access to the navigation controller so has just to add to the stack another controller and push it (the navigation controller still the same for all the VC in the stack)
So what you have to do is stop to handle this cases when you push another controller and restart to do that when you pop back the controller
This bug was caused by the handler mapViewDidFailLoadingMap:withError: being called multiple times before control passed to the pushed view controller. I fixed the bug by making three changes to the handler method:
1) I removed the UIAlertView show and moved it to the pushed view controller;
2) I used a flag (initialized in viewDidAppear:) to return from the handler without executing the push if the handler was being called a second time;
3) I checked for the pushed view controller's existence before allocing it.
When all three of these changes were made, transitions between view controllers happened correctly. Code follows:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
{
[aiView stopAnimating];
if (mapViewFailed > 0) {
mapViewFailed ++;
return;
}
if (mapTypeInt == 0) {
[map setUserTrackingMode:MKUserTrackingModeNone];
[self removeAsNoticationsObserver];
if ([currentMode isEqualToString:#"retracingSteps"])
{
if (!rvc) {
rvc = [[RetraceViewController alloc] initWithNibName:#"RetraceViewController" bundle:nil];
}
[[self navigationController] pushViewController:rvc animated:YES];
}
else{
if (!ttvc) {
ttvc = [[TripTableViewController alloc] init];
}
[[self navigationController] pushViewController:ttvc animated:YES];
}
mapViewFailed ++;
}
else{
[self setMapType:0];
NSString *message = [NSString stringWithFormat:#"Your signal is not currently strong enough to download a satellite map. Switching to Standard Map view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Can't Use Satellite Maps"
message:message
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[av show];
}
}
No doubt a more elegant solution is available which I don't know about yet, being an iOS newb.

iOS Dev: Segue by pressing a button in an Alert?

I've been searching for a few hours and haven't found any answers, so I would really appreciate your help!
I am designing an app and need to segue to a different view controller when a button in an alert is pressed. I already have the alert set up, but just need the code to segue to a new view.
Try this. create alertview first.
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Message" message:#"Open New controller?" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes",nil];
[alertView show];
[alertView release];
Implement the AlertView Delegate Method
#pragma mark AlertView Delegate
-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != alertView.cancelButtonIndex)
{
Viewcontroller *vc = [[UIViewcontroller alloc] initWithNib:#"Viewcontroller"];
[self presentModalViewController:vc];
[vc release];
}
}
Implement the UIAlertViewDelegate and add the method alertView:clickedButtonAtIndex:. Once the right button is clicked, call the method to segue into the new view.
Implement UIAlertViewDelegate and then you get a callback with which button was pressed when the alert was dismissed. Simply call performSegueWithIdentifier in there.
More details at the UIAlertViewDelegate protocol reference

Resources