Any reason why a model tableview (UITableViewController) is not scrolling a table when the keyboard appears ONLY if presented by a popover controller?
I basically have a popover menu with a button that opens a model Tableview fullscreen, text fields within rows at the bottom of the table get hidden by the keyboard when tapped.
This is not the same as a UIView or Scrollview where you have to manage scrolling manually. The UITableViewController has this functionality built in, it just doesn't work if there is a popover controller somewhere back down the hierarchy.
Sample project: www.geoffcoope.co.uk/ios/testTVCScrolling.zip
Thanks
Geoff
Either your UITableView is sending its delegate method requests to the popover's view controller instead of your UITableView controller, or your UITableView's delegate is set to nil. Look where you are instantiating your TableView and make sure you are setting the delegate property correctly.
I guess your problem is with a way a storyboard's segue shows popover controllers. It looks like it is a common problem.
So, the easiest way to solve it, is to move the creation of your modal window inside a code. Something like that:
Inside your ViewController.h write
#property (strong) UIPopoverController * popoverController;
- (void) showNavigationController;
Inside your ViewController.m write
#synthesize popoverController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[super prepareForSegue:segue sender:sender];
if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]]){
if (popoverController) [popoverController dismissPopoverAnimated:NO];
UIStoryboardPopoverSegue * popoverSegue = (UIStoryboardPopoverSegue *) segue;
popoverController = popoverSegue.popoverController;
}
}
- (void) showNavigationController
{
[popoverController dismissPopoverAnimated:NO];
popoverController = nil;
UIStoryboard *ub = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *controller = [ub instantiateViewControllerWithIdentifier:#"navigationController"];
[self presentModalViewController:controller animated:YES];
}
then inside your MainStoryboard set identifier navigationController for your navigation controller and instead of segue on your "Show Table" button set a "touch up inside" delegate. This delegate will execute your showNavigationController method from ViewController (using a NSNotificationCenter, as example)
Related
I'm new to iOS programming and I'm facing a problem
I'm having a problem with custom delegate.
I'm trying to make a simple custom where it return data to the previous view controller and pop the current view controller.
I have 2 navigation view controller
1 - main view controller
2 - Adding
and here is the protocol that is written in the adding view controller
#protocol AddingDelegate <NSObject>
#required
-(void)setInformation:(Adding *)controller withObject:(Conference *)info;
and here is the where I called it in adding view controller
-(IBAction)addingConference
{
NSLog(#"Adding Button Pressed");
conferenceObject = [[Conference alloc]init];
conferenceObject.name = [NameTX text];
conferenceObject.city = [CityTX text];
conferenceObject.description = [Dectription text];
NSMutableArray *info = [[NSMutableArray alloc] init];
[info addObject:conferenceObject];
[self.delegate setInformation:self withObject:conferenceObject];
NSLog(#"adding Conference method is done");
}
I wrote the delegate at the interface in the main view controller
#interface MainViewController : UITableViewController <AddingDelegate>
#end
and here where I declared the delegate method
-(void)setInformation:(Adding *)controller withArray:(NSMutableArray *)info
{
NSLog(#"in the main view at the delegate");
[self.navigationController popToRootViewControllerAnimated:YES];
NSLog(#"Should be popped right now");
}
and this is the prepare for segue method
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"AddObject"]) {
UINavigationController *navigation = segue.destinationViewController;
Adding *addingViewController = [[navigation viewControllers]objectAtIndex:0];
addingViewController.delegate = self;
}
}
now the problem is when I push the adding on top of the stack and then fill the information and press done the adding view controller doesn't pop to show main view controller.
I tried to log everything and the logs from the main view controller doesn't show .
Please help me
What I notice here is that in the implementation of prepareForSegue:sender: the segue's destinationViewController is a navigation controller. This makes me think that your segue is not pushing the AddingController on the current navigation stack but it's presenting a new one instead. This means the new navigation controller containing the AddingController is presented modally and as such, when you try to pop the navigation stack nothing seems to happen because you're operating on the wrong navigation stack. If that is the case you have two options: 1. change [self.navigationController popToRootViewControllerAnimated:YES]; for [self dismissViewControllerAnimated:YES completion:nil]; or 2. change the segue to be a push segue instead of a modal segue and point the segue directly to the AddingController.
In Adding.m
#class Adding;
#protocol AddingDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface Adding : UIViewController
#property (weak, nonatomic) id <AddingDelegate> delegate; // have you forgot this one
#end
and use
[self dismissViewControllerAnimated:YES completion:nil];
You need dismiss if you want get back to previous screen and make sure you have added Navigation controller
I have code that uses Storyboards for seques, like so:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowDiagnosis"])
{
[segue.destinationViewController setHappiness:self.diagnosis];
}
...
But I want to do it programmatically. I have myViewController class and when I click on a button I want to animate and push to myUINavigationController.
How is this done programmatically?
First things first, a segue cannot be created programmatically. It is created by the storyboard runtime when it is time to perform. However you may trigger a segue, which is already defined in the interface builder, by calling performSegueWithIdentifier:.
Other than this, you can provide transitions between view controllers without segue objects, for sure. In the corresponding action method, create your view controller instance, either by allocating programmatically or instantiating from storyboard with its identifier. Then, push it to your navigation controller.
- (void)buttonClicked:(UIButton *)sender
{
MyViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"my-vc-identifier"];
// OR MyViewController *vc = [[MyViewController alloc] init];
// any setup code for *vc
[self.navigationController pushViewController:vc animated:YES];
}
First of all segue can be used only with if you have a UINavigationController that will handle the navigation (push, pop, etc).
So if you have a UINavigationController and you want to push another UIViewController on the stack without using segue then you can use pushViewController:animated: method which also has a reverse popViewControllerAnimated:. Also UINavigationController provides other methods for adding/removing UIVIewControllers, for more info check UINavigationController class reference.
In Xcode 4.5 you can change views from a segue, But I want to programatically change views in Mainstoryboard. I Know in Xib. we use this method:
SecondViewController *Second = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:Second animated:YES];
I want to change the view when the user clicks done in the textfield (This code detects if user clicks done):
-(IBAction)hidekeyboard {
[sender resignFirstResponder];
}
You create a segue in your storyboard that goes from the view that has the text field in question and goes to your SecondViewController. You have to assign an identifier to it (also in the storyboard, click on the line connecting the two view controllers), let's call it "segueToSecondViewController". Then, you can manually call the segue using:
[self.storyboard performSegueWithIdentifier:#"segueToSecondViewController" sender:self];
An alternative to segueing is to instantiate the viewController from the storyboard.
First you assign a Storyboard ID to the viewController in the storyboard.
Then you instantiate that viewController, and present it.
MBTagEditorViewController *tagEditor = [self.storyboard instantiateViewControllerWithIdentifier:#"TagEditor"];
[self presentModalViewController:tagEditor animated:YES];
I am trying to segue to a ABPersonViewController from a button in the Storyboard.
But if I do that the screen is completely black.
If I use a IBAction for the button and use the following code it works:
ABPersonViewController *person = [[ABPersonViewController alloc]init];
[self.navigationController pushViewController:person animated:YES];
Why is that? am I doing wrong?
EDIT: I found a work around but I don't think this is a proper way to do it. I subclassed ABPersonViewController and overrode the initWithCoder method with the following:
-(id)initWithCoder:(NSCoder *)aDecoder{
self = [self initWithNibName:nil bundle:nil];
return self;
}
Since you are using storyboards you should should name the segue you are using and then this in your IBAction:
[self performSegueWithIdentifier:#"abPersonViewControllerSegue" sender:self];
This way you do not even need to manually call alloc/init. Then in your prepareForSegue you could set any attributes:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"abPersonViewControllerSegue"])
{
[segue.destinationViewController setAttribute:#"whatever you want"];
...
}
}
If this is not what you are looking for please let me know.
You don't set the displayedPerson property ...
#property(nonatomic, readwrite) ABRecordRef displayedPerson
Since you are using Storyboard, why do you still call [[ABPersonViewController alloc]init]?The Storyboard will handle the creation and pushing of the ABPersonViewController(if you specified Push action for the segue). As long as you control dragged from the Button to the ABPersonViewController in story board, you do not need to write a single line of code. If you control dragged from the view controller that contains the button, then you should be calling performSegue to trigger the segue(you need to set the identifier for the segue in this case) that adds the ABPersonViewController to the navigation controller.
I have an UISplitViewController with the master having an UIViewController embedded in an UINavigationController. A toolbar button is responsible for bringing an UIPopoverController up, via segue. Such popover controller wraps an UIViewController also embedded in an UINavigationController, called SettingsViewController.
I can get a pointer to the UIPopoverController from the UIStoryboardPopoverSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(UITableViewCell *)sender
{
if ([segue.identifier isEqualToString:#"Settings"]) {
UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue*) segue;
SettingsViewController *settingsViewController = ... // TODO
settingsViewController.popoverController = popoverSegue.popoverController;
}
}
But I can't find a way to get a reference to the inner SettingsViewController. I don't want to use a static field accessible via class method, it would be a terrible workaround.
What am I missing to get it right?
Thanks for your help!
UIPopoverSegue contains a UIPopoverController
UIPopoverController *popoverController = popoverSegue.popoverController;
With this you can easily get the contentViewController (view displayed in the popover)
UINavigationController *contentNC = (UINavigationController *) popoverController.contentViewController;
And from the content navigation controller, you get the actual view controller:
SettingsViewController *settingsVC = [contentNC.viewControllers lastObject];
Does this solve your problem?
I have a very similar setup in my app as described at the beginning of the question.
In my prepareForSegue (and inside where I check the segue.identifier) I grab and set things on the popover's view controller like so:
settingsViewController *settingsPop = segue.destinationViewController;
settingsPop.someVar = self.var;
segue.destinationViewController makes it simple and easy.