Close Popover and open new one with one tap - ios

Apple's "Mobile Human Interface Guidelines" says about Popovers:
When possible, allow people to close one popover and open a new one
with one tap. This behavior is especially desirable when several
different bar buttons each open a popover, because it prevents people
from having to make extra taps.
The only solution I can think right now is to track the position of the touch when dismissing the popover and check whether that was the position of another button.
Is there any easier way to do this?
PS: I searched in stackoverflow and googled quite a while before posting. Sorry, if this was asked here before.
UPDATE
I guess I didn't explain myself well. Let's say I have three buttons. All of them open a popover. My user taps button #1 and a popover opens. While the popover is open, the user taps button #2. The popover gets dismissed (because the user tapped outside of the popover - default behavior of non-modal popovers) and a new popover opens up because the user had clicked on button #2. All of that without having to tap twice: once to dismiss the popover and twice for opening the new one.
2nd UPDATE
I built a simple dummy to reproduce what I'm trying to do. When the user taps on a button and a popover is open, the method that opens the popovers doesn't get called. Therefore the user has to click twice to open the second popover. Any ideas?
#import "RootViewController.h"
#import "AViewController.h"
#interface RootViewController()
#property (nonatomic, retain) UIPopoverController *currentPopover;
#end
#implementation RootViewController
#synthesize currentPopover;
- (void)loadView
{
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView *view = [[UIView alloc] initWithFrame:applicationFrame];
CGRect buttonFrame = CGRectMake(50, 100, 200, 40);
for (int i = 0; i < 3; i++)
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:[NSString stringWithFormat:#"Button %i", i + 1] forState:UIControlStateNormal];
[button addTarget:self action:#selector(openPopover:) forControlEvents:UIControlEventTouchDown];
[button setFrame:buttonFrame];
[view addSubview:button];
buttonFrame.origin.y += 50;
}
self.view = view;
[view release];
}
- (IBAction)openPopover:(id)sender
{
AViewController *avc = [[AViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:avc];
[avc release];
UIPopoverController *tempPopover = [[UIPopoverController alloc] initWithContentViewController:navigationController];
[tempPopover setDelegate:self];
[tempPopover setPopoverContentSize:CGSizeMake(320, 500)];
[tempPopover presentPopoverFromRect:[sender frame] inView:[self view] permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
self.currentPopover = tempPopover;
[tempPopover release];
[navigationController release];
}
- (void)dealloc
{
[currentPopover release];
[super dealloc];
}
#end

If you use bar button items in a toolbar, the open popover is not automatically closed when you tap another bar button item. In these situations you should close the visible popover and open the other one in one step.

- (IBAction)sortAction {
[searchBarView resignFirstResponder];
[self.popoverController dismissPopoverAnimated:YES]; //clear popover
self.popoverController = popoverSetting;
[self.popoverController presentPopoverFromBarButtonItem:sortBarButtonItem
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; //show popover
}
hope will help you

Lets say you have 3 buttons and each opens a popup. You could use a state variable that tracks whether a popup is currently open, and inside each "open popup" method, close the existing one (if it is open) before opening the new popup.

Related

Hide UIButton added in UITabBarController from child ViewController

I have a TabBar application, which I have added a UIButton too, which brings up a menu.
This is how the menu button is added to the Tab Controller:
//TabBarViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.menuBtn = [UIButton buttonWithType:UIButtonTypeCustom];
menuBtn.frame = CGRectMake(285, 28, 24, 24);
[menuBtn setBackgroundImage:[UIImage imageNamed:#"menu.png"] forState:UIControlStateNormal];
[menuBtn addTarget:self action:#selector(showMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.menuBtn];
}
This functions fine and produces the following:
I have the button in the Tab Bar Controller so it shows on each tab.
This all works fine, but when I use the search bar the menu button becomes in the way:
How would I be able to hide the menu button while the search is active?
I have worked out how to detect when the search bar is active, but I am having trouble trying to hide the menu button.
//SearchViewController.m
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
TabBarViewController *vc = [[TabBarViewController alloc]init];
[vc.menuBtn setHidden:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
TabBarViewController *vc = [[TabBarViewController alloc]init];
[vc.menuBtn setHidden:NO];
}
I have tried a few methods, but nothing is able to hide the menu button. Is this impossible, or am I missing something?
Any help would be much appreciated, Thanks
As rdelmar commented:
you need to get a reference to the tab bar controller that you already have, not create a new one with alloc init.
Currently I was making a new TabBarViewController, with this:
TabBarViewController *vc = [[TabBarViewController alloc]init];
Instead, I needed to use a reference. I was able to do this by using 'parentViewController'.
The structure of the app from the TabBarViewController is:
TabBarViewController
SearchNavigationController
SearchViewController
So by using 'parentViewController' twice I was able to reference TabBarViewController, then hide the menuBtn.
The following is what worked for me:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
((TabBarViewController *)self.parentViewController.parentViewController).menuBtn.hidden = YES;
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
((TabBarViewController *)self.parentViewController.parentViewController).menuBtn.hidden = NO;
}

UIPopoverController keeps reloading UIWebView

I have a UIPopoverController that I am loading with a ViewController to hold a UIWebView. When I press a button to present the Popover, the webpage loads fine. I can dismiss the Popover just as fine as well. When I press a button to present the Popover again, the UIWebView reloads. Is there a way to maintain/persist the state of the WebView in the ViewController so that the content remains the same when the popover is presented/dismissed?
Maybe another question is, when a Popover is dismissed, is my ViewController wiped? I don't believe it should be because it isn't a local variable, but I'm not so sure if I have a misunderstaing of how UIPopoverController's behave.
Relevant code below:
In my main ViewController:
_popoverMenuVC = [[TBPopoverMenuViewController alloc] init];
_popoverMenuVC.delegate = self;
_popoverMenuVC.contentSizeForViewInPopover = CGSizeMake(300, 300*(1+sqrt(5)/2.));
_popoverCtr = [[UIPopoverController alloc] initWithContentViewController:_popoverMenuVC];
Code for my button:
- (void)menuBtnPressed:(id)sender
{
if (IS_IPAD) {
UIButton *btn = (UIButton*)sender;
if (!_popoverCtr.isPopoverVisible) {
//set url for popover webview
_popoverMenuVC.popoverURL = #"http://www.google.com";
// show popover
[_popoverCtr setPopoverContentSize:CGSizeMake(300, 300*(1+sqrt(5)/2.))];
[_popoverCtr presentPopoverFromRect:btn.frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionDown
animated:YES];
} else {
// hide popover
[_popoverCtr dismissPopoverAnimated:YES];
}
}
Code for my popoverMenuVC:
-(void) viewDidAppear:(BOOL)animated{
[self setupUIs];
}
- (void)setupUIs
{
// webview
wv = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 300*(1+sqrt(5)/2.))];
//activity indicator
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.activityIndicator.frame = CGRectMake(0.0, 0.0, 40, 40);
self.activityIndicator.center = self.view.center;
// darkview for activity indicator
self.darkView = [[UIView alloc] initWithFrame:CGRectMake(0, 0,300, 300*(1+sqrt(5)/2.))];
[self.darkView setBackgroundColor:[UIColor blackColor]];
[self.darkView setAlpha:0.4];
wv.delegate = self;
wv.scalesPageToFit = YES;
wv.scrollView.bounces = NO; //get rid of bounce for popover menu
[self.view addSubview:wv];
}
A few delegates for handing activity indicator:
#pragma mark Activity Indicator
- (void) showLoadingIndicator
{
[self.view insertSubview:self.darkView aboveSubview:self.view];
[self.view insertSubview:self.activityIndicator aboveSubview:self.darkView];
[self.activityIndicator startAnimating];
}
- (void) hideLoadingIndicator
{
[self.activityIndicator stopAnimating];
[self.activityIndicator removeFromSuperview];
[self.darkView removeFromSuperview];
}
#pragma mark UIWebView Delegates
-(void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(#"Webview is starting to load");
[self showLoadingIndicator];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSLog(#"request: %#", [[webView request] description]);
if(!webView.loading){
NSLog(#"Webview has finished loading");
[self hideLoadingIndicator];
}
}
I have a very similar implementation of this working for iPhone, without the Popover obviously, just with ViewControllers and it seems to work fine. Wondering if this has to work with the PopoverController itself...
Thanks for any help!
Edit 1:
I moved the following code to my main ViewController:
_popoverMenuVC = [[TBPopoverMenuViewController alloc] init];
_popoverMenuVC.wv = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 300*(1+sqrt(5)/2.))];
_popoverMenuVC.wv.scalesPageToFit = YES;
_popoverMenuVC.wv.scrollView.bounces = NO; //get rid of bounce for popover menu
_popoverMenuVC.req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
_popoverCtr = [[UIPopoverController alloc] initWithContentViewController:_popoverMenuVC];
I have nothing in my viewDidAppear function anymore, I now moved it to viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
wv.delegate = self;
[wv loadRequest:req];
[self.view addSubview:wv];
}
But the WebView doesn't load anymore (which I think is related to this issue: iOS 7 UIWebView doesn't load webpage)
Moving the above code from ViewDidLoad to viewDidAppear will load my WebView, yet the same reloading problem persists.
Any additional help would be appreciated!
Edit 2:
I did some code refactoring in order to focus in on what might be causing this behavior:
In my main ViewController, I am setting up my PopoverViewController, along with its WebView, and setting it to be the init content for our UIPopoverController:
_popoverMenuVC = [[TBPopoverMenuViewController alloc] init];
_popoverMenuVC.wv = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 300*(1+sqrt(5)/2.))];
_popoverMenuVC.wv.scalesPageToFit = YES;
_popoverMenuVC.wv.scrollView.bounces = NO; //get rid of bounce for popover menu
_popoverMenuVC.req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
[_popoverMenuVC.view addSubview:_popoverMenuVC.wv];
_popoverCtr = [[UIPopoverController alloc] initWithContentViewController:_popoverMenuVC];
All of this is happening once outside of my TBPopoverMenuViewController's code.
In my TBPopoverMenuViewController.h, I set up a property for wv and req.
In my TBPopoverMenuViewController.m, I synthesize my wv, but not my req.
Here is my viewDidLoad, where I set the wv's delegate to self.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view setBackgroundColor:[UIColor whiteColor]];
wv.delegate = self;
// [wv loadRequest:_req];
}
And in viewDidAppear, this is where I load my request.
-(void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:NO];
[self setupActivityIndicator];
[wv loadRequest:_req];
}
Trying to load my request anywhere else, in view did load, or even in the main ViewController, it doesn't load the request in the popover. It only seems to want to happen in the viewDidAppear function.
Further, when I try to load the popover the second time, you can see the previous wv with the page loaded where it was before, but then almost immediately after the request seems to reload on top of that webview.
Does anyone have a solution? How can I maintain my webview when the popover dismisses and is re-presented. This seems to work fine on iPhone in a non-popover implementation, making me think that there is something I'm missing for iPad/iOS7/UIPopoverControllers.
Any help would be appreciated!
Edit 3:
After some debugging with Xcode's Instruments tool, I've realized that calling:
[wv loadRequest:_req];
In the viewDidAppear of my ViewController results in a memory leak! So there seems to be something essential in my problem with the UIWebView reloading and where/how I'm loading my request. As I've mentioned before, I've tried calling loadRequest outside of my Popover's ViewController in my main View Controller, with no luck. I also tried to call loadRequest in viewDidLoad, with no luck as well.
I was able to successfully call loadRequest in my menuBtnPressed function after the popover is presented:
- (void)menuBtnPressed:(id)sender
{
if (IS_IPAD) {
popOverBtn = (UIButton*)sender;
if (!_popoverCtr.isPopoverVisible) {
[_popoverCtr setPopoverContentSize:CGSizeMake(300, 300*(1+sqrt(5)/2.))];
[_popoverCtr presentPopoverFromRect:popOverBtn.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
[_popoverMenuVC.wv loadRequest:_popoverMenuVC.request];
} else {
// hide popover
[_popoverCtr dismissPopoverAnimated:YES];
}
Which boils down to the two places I can actually successfully call loadRequest for the UIWebView in my popover are:
-in viewDidApepar in the ViewController
-after presenting my popover with a button press
Both of these seem to still result in a memory leak according to Xcode's Instruments tools. I still can't get my UIWebView to maintain/persist state when going back from presenting -> dismissing -> re-presenting the popover.
Any suggestions?
The viewDidAppear method of the popover is called every time the present is done.
So every time the popover is presented, the current code adds a new UIWebView which goes to the default url (with all the previous web views from the previous presents underneath).
A couple of changes should give you the behavior you want:
In the popover content view controller, move the call to setupUIs from viewDidAppear to viewDidLoad.
In the main view controller, move the setting of popoverURL to where the popover content view controller is first created (right after you alloc+init it).
It turns out the answer to my problem as to...call loadRequest twice.
//setup popover menu vc
_popoverMenuVC = [[TBPopoverMenuViewController alloc] init];
_popoverMenuVC.wv = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 300*(1+sqrt(5)/2.))];
_popoverMenuVC.delegate = self;
_popoverMenuVC.wv.scalesPageToFit = YES;
_popoverMenuVC.wv.scrollView.bounces = NO; //get rid of bounce for popover menu
_popoverMenuVC.request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com/"]];
[_popoverMenuVC.wv loadRequest:_popoverMenuVC.request]; //first request
[_popoverMenuVC.view addSubview:_popoverMenuVC.wv];
_popoverCtr = [[UIPopoverController alloc] initWithContentViewController:_popoverMenuVC];
[_popoverMenuVC.wv loadRequest:_popoverMenuVC.request]; // second request
This results in the behavior I want. The UIWebView loads in the PopoverViewController with the page loaded, and when I present-> dismiss-> re-present the popover, the page UIWebView is where I left it. I have no idea why this works though, it seems like a bug or a hack, having to call the function twice. The loadRequest also seems to result in a memory leak according to the Instruments toolset in Xcode.
So I solved my problem, but I'm still a bit confused as to why this is the solution. Luckily I stumbled onto it...
Any clarification by anyone more experienced would be appreciated!

Popover controller not dismiss when click on other bar button but dismiss when click other button(or outside popover)

I show a popover using - "presentPopoverFromBarButtonItem" - after that the popover will not dismiss when I click on the other bar button item in right navigation bar buttons.
But dismiss the popover when I click elsewhere.
Also the issue is not there when we show popover using - " presentPopoverFromRect: inView: " - strange?.
Since we don't get the frame from UIBarButtonItem How can I show a popover correctly from barbutton.
thanks,
Answering my own question to help some one,
// Presenting from BarButton will not dismiss popover when we click on other bar button.
// [self.popoverController presentPopoverFromBarButtonItem:self.barButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
NSMutableArray* buttons = [[NSMutableArray alloc] init];
for (UIView *subview in self.navigationController.navigationBar.subviews) {
if ([subview isKindOfClass:[UIControl class]]) {
[buttons addObject:subview];
}
}
UIView *view = [buttons objectAtIndex:1]; // The '1' is the index to your bar item in the array of .items
CGRect barButtonFrame = [self.navigationController.navigationBar convertRect:view.frame toView:self.navigationController.view];
[self.popoverController presentPopoverFromRect:barButtonFrame inView:self.navigationController.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
What I did above is find the frame of bar button and show popover "presentPopoverFromRect".
I do no,whether you used nib or xib in popover controller or not,but let try below code
if (_Popover != nil) {
[_Popover dismissPopoverAnimated:YES];
}
else {
[_Popover dismissPopoverAnimated:NO];
}

UINavigationController as child of UINavigationController

I don't know if the title explains the question itself but here it is ...
I have a UINavigationController which is the parentViewController of a UINavigationController. The thing is the childViewController behaves strange, when I add it as a child it first has the gap for the statusBar (it doesn't occupy the entire screen) and if I "solve" that "bug" by hiding and showing the navigationBar, the gap goes away but now the child doesn't respect the frame I set manually.
Then I tried to continue and when I presented a modal on the child and dismiss it, the entire child goes away ...
What would be wrong there? The parent-child relationship with both containers or what?
Thanks in advice
EDIT: Here's an example project showing the strange behavior
http://www.mediafire.com/?8saa68daqfkf335
EDIT 2: I found a solution actually and I didn't find it really clear on Apple Docs, it says the childViewControllers take its frame from the parentViewController they belong to, but it doesn't say that if the parentViewController "reappears" (like a push on it) the childViewControllers get resized again by the parentViewController frame ... Hope this helps anyone
I believe it would be better to present the second navigation view controller as a modal view controller.
For example, replace your current presentController selector with something like:
- (void)presentController:(id)sender {
ChildViewController1 *cvc = [[ChildViewController1 alloc] initWithNibName:#"ChildViewController1" bundle:nil];
nc3 = [[UINavigationController alloc] initWithRootViewController:cvc];
nc3.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:nc3 animated:YES completion:nil];
UIBarButtonItem *i = [[UIBarButtonItem alloc] initWithTitle:#"X" style:UIBarButtonItemStyleBordered target:self action:#selector(close)];
cvc.navigationItem.leftBarButtonItem = i;
}
Then, your close selector could become:
- (void)close {
[nc3 dismissViewControllerAnimated:YES completion:nil];
}
(though I'd recommend moving the creation of the button and handling the close actually in ChildViewController1.m).
Of course, this would take all the creation of the navigation controller off ViewController.m's viewDidLoad selector:
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
UIButton *b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
b.frame = CGRectMake(0, 100, 100, 40);
[b setTitle:#"present" forState:UIControlStateNormal];
[b addTarget:self action:#selector(presentController:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:b];
}
Hope it works!

How to get new view from clicking barbuttonitem?

I have made a barbuttonitem programmatically on a toolbar overlay. All of this appears when my camera is accessed. The button functions fine and the selector is fully capable of calling it.
Here's the overlay in .m , take a look at the doneButton selector.
- (UIView*)CommomOverlay {
//Both of the 428 values stretch the overlay bottom to top for overlay function. It
doesn't cover it.
UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,420)];
UIImageView *FrameImg = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,320,420)];
[FrameImg setImage:[UIImage imageNamed:#"newGraphicOverlay.png"]];
FrameImg.userInteractionEnabled = YES;
UIToolbar *myToolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 428, 320, 50)];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self
action:#selector(doneButtonPressed)];
[myToolBar setItems:[NSArray arrayWithObjects: cancelButton, flexiSpace2, flexiSpace,
doneButton, nil] animated:YES];
[view addSubview:FrameImg];
[view addSubview:myToolBar];
return view;
}
Here's method I'm using for my void function.
//NewViewController is the nib I want to bring up when done is clicked.
-(void)doneButtonPressed {
NewViewController *UIView = [[NewViewController alloc] initWithNibName:nil bundle:nil];
UIView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:UIView animated:NO];
[UIView release];
NSLog(#"Acknowledged!");
}
The button is being called, but nothing happens. As you can see I added an NSLog function to see whether it was being called or not. "Acknowledged" shows up in the log when the button is clicked, so it's not a problem with the selector. The app doesn't crash when i use the button either, which should occur had I done some heavily faulty coding (in fact I don't even get any warnings about it), as far as Xcode is considered, everything is fine which, needless to say isn't.
I also have my .h files of NewViewController in place correctly in the view controller in which this code is being implemented.
UIView is the name of a class. Don't use it as the name of a variable. At best it is hugely confusing and is a probable reason for your problems. Rename UIView to something like myView everywhere in doneButtonPressed.

Resources