iPhone UIActionSheet called from UITabBar Button - iphone

Is there a way to call an ActionSheet directly from a tabbar. I'm working on a program where the user wants a contact button on the tab bar that displays an actionsheet with the appropriate buttons.
Thanks in advance

Couldn't you just have a view controller associated with one of the tabs, and then leave its view plain, and in viewDidLoad make the actionSheet?

I agree with all the people above saying that this isn't a good idea (and that this should be done using a toolbar), but it's definitely doable.
The code below implements one of the UITabBarControllerDelegate methods, and avoids the selection of the tab bar item, and instead creates and displays a UIActionSheet:
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController
{
NSInteger choice = 0; // --> index of the view controller that should "act as button"
if (viewController == [tabBarController.viewControllers objectAtIndex:choice])
{
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:nil
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:#"Send e-mail", #"Twitter", #"Whatever", nil];
[sheet showFromTabBar:tabBarController.tabBar];
return NO;
}
return YES;
}

An example would be the mail app were your viewing an email and you hit curved arrow button to bring up the "forward - reply" action sheet.

I think I can understand what Steve's after. I have a main activity reached via the 1st element in the TabBarController. I'd like the 2nd tab-bar element to selectively add either a modalview email, an access to facebook, or an access to twitter. It would be nice if that choice is offered via an actionsheet so as to not lose sight of what's "behind" from that first view controller (from the first tab-bar choice) and THEN the new view controller handling the choice shows up. This seems to be what "AP mobile" does when you want to 'share' a news article, for example.
#Adrian : I couldn't get your solution to work out-of-the-box.. but then found out why (read on...)
It doesn't help (obviously) to specifically drag from the Outlet:delegate to file-owner
You'll get:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Changing the delegate of a tab bar managed by a tab bar controller is not allowed.'
My delegate method was being ignored until I added the UITabBarControllerDelegate to the interface definition (UIApplicationDelegate was already present and I didn't read further)...
AND
In applicationDidFinishLaunching I added
[rootController setDelegate:self];
Cheers.

Related

Viewcontroller fails so call another Viewcontroller - Objective-c

I have two ViewControllers one named ViewController and another named Signup,
Here is the code I use to call the Signup.xib file,
- (IBAction)signupButton:(id)sender {
Signup *myView = [[Signup alloc] initWithNibName:#"Signup" bundle:nil];
[self.view addSubview:myView.view];
}
This code works. It starts up the Signup Viewcontroller but When I try to call the ViewController.xib file from the Signup.m It doesn't work.
Here is how I call the ViewController.xib file from the Signup.m file,
- (IBAction)loginView:(id)sender {
ViewController *dashboardView = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[self.view addSubview:dashboardView.view];
}
I have imported the ViewController.h file within the Signup.m file.
The loginView button works because I placed an NSLog() in the button to see if it worked and it does. But When I click the loginView button I get some type of error. I don't know what errors are in xcode but its something like this,
0x110309b: movl 8(%edx), %edi <- Thread 1: EXc_BAD_ACCESS (code=1, address=0x60000008)
It is highlighted in green and I don't know what it means.
As user Priyatham51 said, number 3 is the reason why you are getting the error. The alternative, IF you are not using UINavigationController to push/pop views, is to to present the Signup view controller as a modal view. Then when you want to go back, you can call inside the Signup controller.
[self dismissModalViewControllerAnimated:YES];
However you need to change your signup button to.
- (IBAction)signupButton:(id)sender {
Signup *myView = [[Signup alloc] initWithNibName:#"Signup" bundle:nil];
[myView setModalPresentationStyle:UIModalPresentationFormSheet]; //you can change the way it is presented
[myView setModalTransitionStyle:UIModalTransitionStyleCoverVertical]; //you can change the animation
[self presentModalViewController:myView animated:YES]; //show the modal view
}
I hope this helps you.
Try this to present Signup view to user :
- (IBAction)signupButton:(id)sender {
Signup *myView = [[Signup alloc] initWithNibName:#"Signup" bundle:nil];
[self presentModalViewController:myView animated:YES];
}
And back to Login view by dismissing the signup view controller
- (IBAction)loginView:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
You don't generally add View Controller's views to your current view, but if it is what you want to achieve then try cleaning project first. Sometimes XCode fails to properly load nib files after you add/remove them. Go to Product menu-> "Clean", then hold down "Option" key, go to Product Menu and press "Clean Build Folder".
Also, add "All Exceptions" breakpoint. It will be on the left side panel of XCode, under tab "Breakpoints". On that tab there is "+" button at the bottom. Click "Add Exception Breakpoint" and then just press "Done". This basically will give you more information about your crash.
I know that this doesn't exactly help to fix your problem, but will give you more details on it.
The main reason why your code is not working because you are trying to add your ParentView to the subview as a child again and you are not supposed to do that.
It seems like your are trying to do this.
You created your LoginView
You created your SignupView and added it as subview to LoginView. So the LoginView is parent and signup is child.
(Here is your mistake ). You are trying to add the LoginView as the subview of the SignupView. Which is never going to work.
Try doing this
I would suggest you create your LoginView first and have button "SignUp". So when a user clicks on signup "Push" the new SignupView Controller on top the loginViewController. And have a button "Login View" on your signup view . When user click on that button try to "dismiss current view controller". So user will be shown the login view again. You can use this as work around.
Please take a look at this example
http://www.roseindia.net/tutorial/iphone/examples/pushView.html
Here one more working example
http://www.theappcodeblog.com/2011/08/15/iphone-development-tutorial-present-modal-view/

iPhone: How to show pop-up windows and dismiss them

In my Universal App I have a long UITableView with custom cells..and for some cells I may need to show some long pop-up explanaiton about that cell when for instance user clicks a "i" label on the cell. In iPad popover view seems excellent choice for this, but don't know how can I implement this on iPhone, what are the possibilities? Also I want to spend as less time as possible when making it work for iPad- popover view. I want to re-use some of the code or logic i use on iPhone
Things came up to my mind;
-Show explaination in alert shild, but the current look and feel of alert shield is ugly can I customize it however I like and show wherever I line on screen and if I can make it scrollable;
-Or maybe I can make a uitextview to show on top, but then how will I dismiss it, I will need some buttons there..which sounds tricky.
-UIActionsheet with a uitextview on it, is reasonable here?
Also I found this code in S.O but dont know how to use this in my case;
newView.frame = CGRectMake(60, 140, 200, 200);
[parentView addSubview:newView];
Have a look at http://iosdevelopertips.com/open-source/ios-open-source-popover-api-for-iphone-wepopover.html. It's a Popover component for iPhone. I think it works best in your case. You can Google "iphone popover" for more options.
We built an open source library for iPad-like popovers on iPhone allowing you to customise the look and feel of the popovers and place any view or controller inside it.
Watch the project on Github and download it at http://www.50pixels.com/blog/labs/open-library-fppopover-ipad-like-popovers-for-iphone/
On dismissing it, see the following instructions:
Know when a new popover is displayed
- (void)presentedNewPopoverController:(FPPopoverController *)newPopoverController
shouldDismissVisiblePopover:(FPPopoverController*)visiblePopoverController;
Use this delegate method to know when a new different popover is displayed. If you want to dismiss the old popover, and release it, send the dismiss message inside this method.
- (void)presentedNewPopoverController:(FPPopoverController *)newPopoverController
shouldDismissVisiblePopover:(FPPopoverController*)visiblePopoverController
{
[visiblePopoverController dismissPopoverAnimated:YES];
[visiblePopoverController autorelease];
}
Know when the popover is dismissed
- (void)popoverControllerDidDismissPopover:(FPPopoverController *)popoverController;
Use this delegate method to know when the popover is dismissed. This could happen when the user taps outside the popover or when a dismiss message is sent by other actions.
Typically if you used a UIPopover on the iPad you use present a Modal view controller on the iPhone.
So if you create a subclass of UIViewController (e.g. called MyViewController), with the necessary subviews such as a UILabel.
MyViewController *infoViewController = [[MyViewController alloc] init];
//pass data to the new view controller, e.g.
//[infoViewController setInfoText:...];
[self presentModalViewController:infoViewController animated:YES];
[infoViewController release];

Is there a way to stop a UIViewController from being popped from a UINavigationController's stack when the backBarButtonItem is tapped?

I have a UINavigationController with a root view controller and then I push a UIViewController onto the navigation controller's stack. When the user taps the backBarButtonItem I'd like to be able to have an alert view pop up if there are certain conditions met and cancel the pop of the view controller. For example, the user can make certain selections but some combination of them may be invalid so I want to notify them to make changes.
I know that I can prevent the user from making an invalid combination or have an alert view pop up when the invalid combination is selected but I'd rather not do that. The user may be changing selections and may be aware that a certain combination is invalid but I'd rather let them select something that makes the combination invalid then go change something else (and notify them if they haven't made changes before trying to go to the previous screen). For example, if I prevent them from selecting something that makes an invalid combination then they may have to scroll up on a screen, change something, then scroll back down instead of making a selection then scrolling up and changing something.
Using viewWillDisappear: doesn't work because, although I can produce an alert view, I cannot figure out a way to prevent the pop from occurring. The alert view displays but the view controller still pops and they are back to the root view controller (with the alert view displaying).
Is there a way to prevent the pop from occurring? If not, is this something worth filing a bug report about or is this unnecessary and/or esoteric?
You can replace the back button with your own that calls the method you want in loadView
cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(onCancelButtonSelected:)];
self.navigationItem.leftBarButtonItem = cancelButton;
then later
-(void)onCancelButtonSelected:(id)sender {
if (!hasSavedProduct)
{
cancelAlert = [[UIAlertView alloc] initWithTitle:#"Product not saved"
message:#"Exit without saving?"
delegate:self
cancelButtonTitle:#"Exit"
otherButtonTitles:#"Save",
nil];
[cancelAlert show];
[cancelAlert release];
}
then let them go
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if ([cancelAlert isEqual:actionSheet])
{
if (buttonIndex == 0)
{
NSLog(#"Fine. Exiting without saving");
[self.navigationController popViewControllerAnimated:YES];
}
else
{
NSLog(#"Save here");
}
}
This sounds like something more appropriate for a Modal View Controller than it is for a View Controller on a Navigation stack.
If you're married to doing it on the stack though, it'd be nice if you could do this with UINavigationControllerDelegate, but you can't.
Is it possible to set the Back button to disabled, until the entries are valid? Perhaps when the user tries to enter something, but it's invalid, near the top of the View you have a Label animate into place with red text that tells the user they need to fix it. Meanwhile the back button is disabled and it's re-enabled after they make corrections.
Or get really creative with how your UI controls work to ensure that the user can never enter bad data.
Let them go back, just don't save anything unless it's completely valid. That's typically the approach Apple takes.

iPhone ModalViewController For Table View Open by Button?

App Description: I have a UIWebview and a Toolbar beneath it. A button on the toolbar should bring up a modal table view, but it does not.
The toolbar has four buttons:
Previous: Goes to previous site
Next: Goes to the next site (these two being different than the default goForward and goBack methods)
Menu: Display a modalViewController with a TableView of all the available sites (sites will be limited to an array of site links that the next and previous buttons cycle through)
Refresh: Refreshes current site
Application has four main classes/files
WebAppDelegate.h and .m
ListViewController.h and .m (which has the Table View in its xib and the code to fill the table in the .m/ be the modal view controller)
There is only one warning and no errors.
Warning: 'WebAppDelegate' may not repsond to '-presentModalViewController:animated:'
When I run the program, everything is fine until I click the Menu button. I receive this runtime error
[WebAppDelegate presentModalViewController:animated:]: unrecognized selector sent to instance
Below is the code for the Menu button, which is currently in WebAppDelegate.m
-(IBAction)menu:(id)sender {
ListViewController *aListView=[[ListViewController alloc] initWithNibName:#"ListViewController" bundle:[NSBundle mainBundle]];
[self setListController:aListView];
aListView.modalTransitionStyle=UIModalTransitionStyleCoverVertical;
[self presentModalViewController:aListView animated:YES];
[aListView release];
}
Any ideas on what causes the application to crash and why the modal table view does not display?
The error message tells you exactly what your problem is - you're sending a selector to an object that doesn't respond to that selector.
[self presentModalViewController:aListView animated:YES];
self in this case in your instance of WebAppDelegate, which is probably a subclass of NSObject, not UIViewController. presentModalViewController:animated: is a method on UIViewController, so you need to send that message to whatever view controller is currently displayed (or perhaps a navigation controller) if you want to present another view controller modally.
Do not ignore compiler warnings - the one warning you have is likely telling you that...
'WebAppDelegate' may not respond to '-presentModalViewController:animated:'
...which, again, is exactly your problem.
[self.navigationController presentModalViewController:aListView animated:YES];

Remove delegate on dealloc from unreferenced object

If learned the hard way that you should remove the delegate from an object if the life span of the delegate is shorter than the object. But how do you do this if you don't have a reference to the object anymore?
In my iPhone application I have a view controller vc which performs an asynchronous activity and is displayed as a modal view. A cancel button dismisses the modal view. If an error occurs, an UIAlertView alert is displayed. If the user taps on ok, both alert and the modal view disappear. Therefore vc is set
as delegate for alert and implements alertView:didDismissWithButtonIndex:. Something like this:
// UIViewController vc
...
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Error"
message:#"Something went wrong"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
self.alertView = alert; // needed to unset alertView.delegate in dealloc
[alert show];
[alert release];
...
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self dismissModalViewControllerAnimated:YES];
}
}
Normally, the alert view blocks all input. Unfortunately, it fails to do so in some edge cases.
If the user touches down on the cancel-button before the alert view appears and touches up after the alert view appears, the view is dismissed, but not the alert. vc gets deallocated and if the user then taps "ok" on the alert, the application crashes because a message was sent to the released object.
I solved this by assigning alert to a property of vc so I can set alert.delegate to nil in dealloc. I find this solution to be not very elegant, because I don't really need the reference to alert.
Is there a better approach?
Edit: Added the text in italics as clarification
Although, normally an alert view is presented over non changing content. So if the delegate is alive when the view appears it will likely be alive when it's dismissed. If that's not the case, you have to do exactly what you did, and unset the alert view's delegate manually if you no longer care about it's result.
So you do care about the alertview since you care about it's delegate method. The wrinkle is that the delegate may not apply by the time the alert is dismissed. So you need logic there, and for that logic you need to save a reference to the alert view in question.
In other words, you are doing it right. Although, it may have been helpful if UIAlertView retained it's delegate, but it doesn't seem like it does if it crashes when it's dismissed.
Lastly, I thought alert view blocked all screen input? If not, you can make it truly modal by setting vc.view.userInteractionEnabled = NO when the alert appears and switch it back when it's dismissed. This way the user can't dismiss the controller while the alert view is up. Which sounds a bit more sane to me.
From a UI perspective, when an alert view is present, it should demand the user's full attention. Perhaps you might consider disabling the Cancel button (as well as any other visible non-alert-view widgets) when there is an alert view present, and adding a button title to the UIAlertView instance which performs the same task. This would provide a clearer user interface and should also neatly solve your memory issue.
-(void)delayedDismiss {
[self dismissModalViewControllerAnimated:YES];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self performSelector:#selector(delayedDismiss) withObject:nil afterDelay:0.0];
}
If you no longer care about the results of the UIAlertView you should probably also dismiss it in the - (void) viewWillDisappear:(BOOL)animated