Displaying NSPopOver - objective-c

I have a class which is derived from window controller with xib which has all the functionalities. In this xib i have a tableview which displays the list of halls. If i double click on a hall name, i ll get a popover, which displays the features of that hall. I have a view controller class, in which i would ilk to create the pop over programatically,
NSPopover *popover;
NSViewController *popoverViewController;
-(void)displayPopover{
popover = [[NSPopover alloc] init];
[popover setBehavior: NSPopoverBehaviorApplicationDefined];
[popover setDelegate: self];
popoverViewController = [[CHBPopover alloc] initWithNibName: #"MYViewController" bundle: nil];
[popover setContentViewController: popoverViewController];
[popover setContentSize: popoverViewController.view.frame.size];
[popover showRelativeToRect: NSMakeRect(700, 400, 5, 5)
ofView: [[NSApp keyWindow] contentView]
preferredEdge: NSMaxXEdge];
}
In my window controller class, i have a method like,
-(IBAction)featuresDisplay:(id)sender{
if([_hallNamesList selectedRow] == -1){
[self setFeaturesList:nil];
}
else {
//[self.hallFeaturesPopOver showRelativeToRect:[_hallNamesList frameOfCellAtColumn:0 row:[_hallNamesList selectedRow]] ofView:_hallNamesList preferredEdge:NSMaxXEdge];
// [pop.displayPopover ];
NSDictionary *hallFeaturesDictionary;
hallFeaturesDictionary = [_hallNames objectAtIndex:[_hallNamesList selectedRow]];
_hallId=[hallFeaturesDictionary valueForKey:#"hallId"];
[officeDetails setHallName:[hallFeaturesDictionary valueForKey:#"hallName"]];
_featuresList=[conferenceHall getConferenceHallFeaturesWithDetails:officeDetails];
NSLog(#"features list=%#",_featuresList);
[self setFeaturesList:[conferenceHall getConferenceHallFeaturesWithDetails:officeDetails]];
}
}
How would i call that popover method in this IBAction? I need to double click on a row and display the pop over.. How would i do this? Thanks.

The display coordinates seem to be quite off. The rectangle for the popover is relative to the ofView parameter. Start with (0, 0) here, which should display the popover in the upper left corner of your keywindow.contentView. Then fine tune the position. You probably have to pass in the actual rectangle from your IBAction (probably the table cell bounds). The size part in the view rect for the popover is important because the popover moves relative to that. I'd also pass in the view to attach the popover to, as sometimes a view does not become key when you click it.
Note that the delegate for the popover is only necessary if you plan to implement detaching it into a floating window. Experiment also with the behavior. Start with NSPopoverBehaviorTransient. I'm not sure what you actually have to do with the application defined behavior but at least with the transient behavior it works fine for me.
Finally, you don't need to recreate the popover every time you show it. Just set it up in awakeFromNib and then just call showRelativeToRect... every time you need it. With the transient behavior it will automatically disappear.

Related

UIView inside a UIViewController or better way to do it?

I have a problem on how to properly do a certain kind of action.
The image below shows a UIViewController, but the second part of the view is a custom UIView (the one with the profile pic, name and Show View button).
The subclassed UIView is allocated using this code:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
[self.view addSubview:profileView];
The problem is of course, that the button on the UIView can't show any view, since it's only the UIViewController that can push another ViewController to the window(correct?).
The UIView is used in several places in the app and needs to be added easily and have the same behavior across the application.
This worked great until I added the button, and I'm starting to think I've made this wrong, and there has to be a better way to do it (maybe change the UIView to something else?).
I was thinking I should be able to call:
self.superview
And then somehow get the ViewController so I can push another ViewController into the view hierarchy, but nope.
Any suggestions and a tips on how to do this correctly?
UPDATE:
I have no idea on how to push another UIViewController from the button.
What should I do in this method when pressing the button in the UIView:
- (void) showViewButtonTouched:(UIButton*)sender {
GPProfileSocialFriendsViewController *friendsSettings = [[GPProfileSocialFriendsViewController alloc] init];
}
How do I push GPProfileSocialFriendsViewController?
Your - (void) showViewButtonTouched:(UIButton*)sender method should be in your controller and would probably be better named - (void) showView:(UIButton*)sender or - (void) showProfile:(UIButton*)sender so it clearly denotes what it does (not how you got there).
It's not the view's responsibility to manage transitions from a state to another. If you move your method to your controller, your problem is no more (you can easily access self.navigationController or push directly if you don't have an navigation controller like this:
[self presentViewController:vcThatNeedsToBePushed animated:YES completion:nil];
I think you can create weak reference in GPProfileView on UIViewController. Like this:
#property (weak, nonatomic) UIViewController *rootController;
when you create GPProfileView, assign rootController-property:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
profileView.rootController = self; // if view created in view controller
[self.view addSubview:profileView];
and in implementation of button selector:
self.rootController push... // or pop
May be this not correct, but you can try
You could let the view controller push the next view controller when the button is pushed. The view controller can add a target/action on the button, so that the action method in the view controller is called on the touch up inside event.

Superview is Nil after Second Launch of View Controller

I've spent hours and I cant figure this out. I have a detail view controller (UITableView) which is launched here:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
EventLocationDetailController *newDetailViewController = [[EventLocationDetailController alloc] initWithNibName:#"EventLocationDetailController" bundle:nil];
self.eventDetailController = newDetailViewController;
[self.navigationController pushViewController:self.eventDetailController animated:YES];
[newDetailViewController release];
}
In the detail view controller there is a button method which calls the below method to display a slide-in-slide-out animation confirming the users choice:
-(void)confirmLastActionWithMessage:(NSString *)message {
ConfirmActionViewController *newConfirmActionViewController = [[ConfirmActionViewController alloc] initWithNibName:#"ConfirmActionViewController" bundle:nil];
self.confirmActionViewController = newConfirmActionViewController;
[newConfirmActionViewController release];
[[self.view superview] addSubview:self.confirmActionViewController.view];
}
Protocol method called by the ConfirmActionViewController indicating that the animation is finished.
-(void)didFinishConfirmDisplay:(UIView *)viewToRemoveFromSuperview {
[viewToRemoveFromSuperview removeFromSuperview];
}
This works perfect the first time I press the button. If I pop the detail controller and push it back on to the stack and press the button again, nothing happens and the detail controller's superview is nil every time I invoke the method after that. Superview is not nil in the viewWillAppear method for the detail view, only when It gets to the confirmLastActionWithMessage method. No other user interaction happens in between. How do I get the superview back? I have a similar code that works without animation.
I've also noticed that the detail view controller hasn't called dealloc when popped off the stack. Not sure where the problem is.
Thanks.
EDIT 1
OK. I replaced the addSubview line with this one:
[self.view insertSubview:self.confirmActionViewController.view atIndex:0];
and the animation view appeared underneath one of the table cells. Could one of the table cells steal the superview?
Well I don't really understand why you should add the subview to the superview. Why not add it just to self.view
I may not be able to explain why there is no superview but try either adding the controller view to self.view or
[[[[UIApplication sharedApplication] delegate] window] addSubview:yourview];
This will render the view on top of everything.

Using buttons on different views and classes to trigger view switch from the main view controller

I am using storyboard to create my page.. each with it's own class... from the mainViewController, I manage the view change with a swipe gesture recognizer... So far so good... But I have certain pages that will appear as "popup" when swiping up and to get rid of them, the user clicks on the X to remove the view... the thing is that doing this and releasing the view from superview is giving me a white screen as the switch is not done by the mainViewController, since the view is release by the popup class... I think I need to use some sort of delegation to do this.. but my brain just doesn't want to sink in how to use the delegate thing, even after reading about it...
Since the view switching is done on index 1, i'd figure that if I put those popup on index 2 and release them, the view at index 1 would still be there, but.. no..
... so at the beginning of my swipe gesture function, I start declaring the animation process... then I have a switch...case that check for the gesture being done.. left will set the animation to curlUP and right to Curl down... this is what happens after the switch... I also put myView into a myViewTemp, and add the new view to myView in the switch..case statement..
if (myView.title == #"popup1") {
[myView viewWillAppear:YES];
[myViewTemp viewWillDisappear:NO];
// [myViewTemp.view removeFromSuperview];
[self.view insertSubview:myView.view atIndex:2];
[myViewTemp viewDidDisappear:NO];
[myView viewDidAppear:YES];
} else {
[myView viewWillAppear:YES];
[myViewTemp viewWillDisappear:YES];
[myViewTemp.view removeFromSuperview];
[self.view insertSubview:myView.view atIndex:1];
[myView viewDidDisappear:YES];
[myViewTemp viewDidAppear:YES];
}
[UIView commitAnimations];
}

UIView transitionWithView a subview with multiple xib files

I'm attempting to create a subview inside a regular view. When a button is clicked that is contained in this subview, it will flip over and show a new subview. This will is meant to simulate a card flipping over on a table. I have a DetailViewController that is an empty xib, but when it's loaded it loads another controller called CardFrontViewController that actually contains the subview (front of the card) all contained in a xib. Once the button inside the subview, it should load the subview that is contained inside the CardBackViewController xib.
Inside DetailViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (!self.cardFrontViewController)
{
self.cardFrontViewController = [[CardFrontViewController alloc] initWithNibName: #"CardFrontViewController" bundle: nil];
[self.view addSubview: self.cardFrontViewController.view];
}
[self configureView];
}
Inside CardFrontViewController
- (IBAction)flipToAnswerCard:(id)sender
{
if (!self.cardBackViewController)
{
self.cardBackViewController = [[CardBackViewController alloc] initWithNibName: #"CardBackViewController" bundle: nil];
}
[UIView transitionWithView: self.cardFrontContainerView
duration: 1.0
options: UIViewAnimationOptionTransitionFlipFromTop
animations: ^{ [self.cardFrontSubView removeFromSuperview];
[self.cardFrontContainerView addSubview: self.cardBackViewController.cardBackSubView]; }
completion: nil];
}
When I click the button to flip over the card, the card flips, but the view that is displayed is empty, meaning that the self.cardBackViewController.cardBackSubView is not loaded inside.
If I do [self.cardFrontContainerView addSubview: self.cardBackViewController.view] inside the flip card method it loads the entire xib inside, but I really just want a view inside. I'm not sure what to do here.
You don't need a UIViewController if all you want is just to load a view, with some IBOutlets.
Just use [[NSBundle mainBundle] loadNibNamed:#"myViewNib" owner:self options:nil] from your DetailViewController, and if IBOutlets corresponding to ne 'card' NIB exist, they will automatically be linked (to the owner).
If you really want a different class to handle this 'card' (and 'own' the IBOutlets) does it necessarily need to inherit from UIViewController? NSObject can sometimes be a good candidate too.
Finally, If you really really want to nest UIViewControllers, this will only work in iOS5.
To support previous iOS versions, you would eventually have to manually call some UIViewController's life-cycle methods (willAppear, didAppear...), pass certain events from you master UIViewController to its detailViewController (like willAnimateToRotation), and maybe some other tricks.
If you just want to flip subviews within a main view, the answer that worked for me was found here: https://stackoverflow.com/a/9028156/1319615

UISplitViewController non root, forcing custom rotation methods, makes master view dissappear

I'm trying to add a split view inside of a tab bar, and since the split view isn't the root, it doesn't properly get the rotation notifications, so the delegate's methods are never called to add the button to the toolbar in the detail view.
I've rigged it up so I can generate the popover when rotated, but when this method is called, the view dissappears from the landscape mode, and if you activate it and then rotate back into landscape, it's a black empty box where the master view used to be. How do I get rid of this occuring?
-(void) displayPopover:(id)sender
{
//Toggle the popover: if it's showing, hide it
if (popoverController != nil && [popoverController isPopoverVisible])
{
[popoverController dismissPopoverAnimated:NO];
}
else
{
//Create a Popover displaying the master view
if (popoverController == nil)
{
popoverController=[[UIPopoverController alloc] initWithContentViewController:self->rootController];
popoverController.popoverContentSize=CGSizeMake(300, 500);
}
[popoverController presentPopoverFromBarButtonItem:[detailController.toolbar.items objectAtIndex:0] permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
You will need to remove all the objects from window using:
[appdelegate window ] subviews] objectAtIndex:0] removeFromSuperview];
Then add your splitview to the window, you can get the view callbacks.
I would recommend either finding a way to get your SplitViewController to be root, or creating a custom subclass of the UISplitViewController that allows for non-root placement.
I really like what Matt Gemmell did here: http://mattgemmell.com/2010/07/31/mgsplitviewcontroller-for-ipad
Using a custom subclass like Matt's will allow you to benefit from all the same delegate methods that a SplitView as root would allow. I used it in a project where I wanted my SplitView to appear as a modal - almost impossible with a traditional UISplitViewController.
so your split view has rotation enabled (shouldAutorotateToInterfaceOrientation:) now you have to make sure that the tab controller has also rotation enabled (should be the appDelegate, am I right?) AND you have to make sure that every other view that is in your TabBar has also rotation enabled!
so if your TabBar contains 2 tabs you have to set the rotation in 3 classes.