Here is the problem I am having. I am unable to set the UINavigationBar title for the views I have contained within a UIPageViewController.
The basic architecture of the app is as follows.
The root view controller for the app is a UITabBarController, with 5 navigation controllers contained in it.
The first Navigation controller, which is the one I am having issues with, contains a page view controller and this page view controller contains a number of UIViewControllers.
I want that, when I swipe through each of these view controllers, I can set the title in the UINavigationBar.
I have tried the following:
In the UIViewController contained within the page view controller, I have tried [self setTitle:#"Title I want"] - it didn't work.
Within the same UIViewController I have also tried [self.navigationBar.navigationItem setTitle:#"Title I want"] - this also didn't work.
I also tried setting the title for the View controller and attempted to extract that inside the PageViewControllers delegate method transitionCompleted, but this didn't work either.
I am wondering should I go back to the drawing board, and whether I am going down a rabbit hole with this view layout architecture. Has anyone else encountered this issue and if so, how did you solve it?
Edit: I must also add that I am doing this programatically.
Thanks for the help.
So, in the end I came up with a way to get this working, albeit not the cleanest solution that I wanted, but suitable for the purpose nonetheless.
I created a new class called PageLeafViewController and set up its init method as below. Child view controllers of a page view controller inherit from this. Here is the code.
Code sample
- (id)initWithIndex:(NSUInteger)index andTitle:(NSString *)navBarTitle; {
if(self = [super init]) {
self.index = index;
self.navBarTitle = navBarTitle;
}
return self;
}
These can be initialised like so before being added to the UIPageViewController.
Code sample
ChildViewController *aChildViewController = [[ChildViewController alloc] initWithIndex:1 andTitle:#"A Title"];
You will need to add a UIPageViewControllerDelegate to your interface for your page view controller. This is so you can implement the code for the delegate methods for when your view transition has been completed, and you need to set the title.
When the UIPageViewController loads, I grab the first view controller and get its title, setting it to the UINavigationController navigation bar
Code sample
PageLeafViewController *initialViewController = (PageLeafViewController *)[self viewControllerAtIndex:0];
[self.navigationItem setTitle:initialViewController.navBarTitle];
When a transition occurs, we set the title again to that of the new child view controller, when the transitioning into view has completed.
Code sample
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
PageLeafViewController *currentLeaf = (PageLeafViewController *)[self.pageViewController.viewControllers lastObject];
[self.navigationItem setTitle:currentLeaf.navBarTitle];
}
Note: The above gets called automatically when a new child view controller has been displayed.
While this is not the most elegant solution it works for now, and I don't think its possible to call a function from within a child view to update the NavigationBar title, unless someone wants to correct me?
Hope this helps.
I don't think you're supposed to set the title on the navigationBar, have you tried self.navigationController.title = #"Title"; ?
Related
I am writing an application with help screens. As per my requirement I need to show the help screens when the view appears for the first time and I am placing the code on the tabbar controller class as (void)viewDidLoad:
{
[super viewDidLoad];
XDKWalkthroughController *sc = [[XDKWalkthroughController alloc]
initWithView:self.view];
[self addChildViewController:sc];
[self.view addSubview:sc.view];
sc.delegate = self;
[sc start];
}
When I am placing this code I am getting one more tabbar item along with existing tabbar item. How to avoid that in my case?
And same thing when I placed in the first view i.e in the first tab view controller view did class. Both navigation bar and tabbar are pushing the child view back.
I have tried these two scenarios, please help me with the possible solution.
This is puzzling me.
The context
The original tutorial I'm following.
Where the segue is added to the Main View via a custom segue:
- (void) perform {
MainViewController *source = (MainViewController *)self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
for(UIView *view in source.main.subviews){
[view removeFromSuperview];
}
source.currentViewController = destination;
destination.view.frame = CGRectMake(0, 0, source.main.frame.size.width, source.main.frame.size.height);
[source.main addSubview:destination.view];
}
The TextField is connected as delegate in the child View Controller. All things being equal I get the app crashed without any message.
The workaround
In the Main View Controller, in -(void)prepareForSegue: I've added [segue.destinationViewController setDelegate:self]; in the meantime I've added a property in the child View Controller id<UITextFieldDelegate> delegate and modified the textfield delegate as self.delegate.
This works, but the trouble is that I've to set the delegated methods in Main View Controller which is not quite efficient as I have more View Controllers to add.
The Objective
How do I set each View Controller to be the delegate for itself without crashing?
The immediate cause of your error is that the view controller that your views belong to is being deallocated. The fact that your views are on screen while their view controller is deallocated highlights a fundamental flaw in the approach of taking views off one view controller and adding them to another. View controller containment is the correct way to solve an issue like this.
Changing the currentViewController property to strong will fix the memory management issue you're seeing, but it's just a bandaid. Your currentViewController will still be missing rotation methods, appearance and disappearance methods, layout methods, and so forth. View controller containment ensures these methods get called for the view controller whose views are on screen.
Here is an altered version of your project that illustrates how to use view controller containment. I think that will be a better solution than manually removing and adding subviews of the view controllers themselves. See the Apple docs for more info on custom view controller containers.
At first, let's see crash report. Please, do the following:
1. Add Exception Breakpoint
2. Edit it as in the picture
You should create a custom class for the destinationViewController wich will implement UITextFieldDelegate
#interface DestinationViewController <UITextFieldDelegate>
#end
And from storyboard add the class to UIViewController that has TextField
And make the connections for elements and TextField delegate.
Implement delegate methods.
You will not need the implementation of prepareForSegue: anymore. You will have two different classes with different elements. Only if you need to pass something from source to destination then you use prepareForSegue:
Hope you'll understand
Is there a method to know when a UIView was pushed by the user from the More view of a UITabbar ?
I have multiple views in a UITabBar and some end up in the More view where they are listed. Il'd like to be able to know when the view is pushed from this More view as opposed to when it is pushed from a UITabBarItem
Thanks !
You can just check if the controller's navigation controller is the more navigation controller:
-(void)viewDidLoad {
[super viewDidLoad];
if (self.navigationController == self.tabBarController.moreNavigationController) {
NSLog(#"Launcehed from more");
}else{
NSLog(#"Launcehed from tab bar");
}
}
Your question says UITabBar but I suspect you mean UITabBarController. Based on how some options "end up in the More view", which is provided automatically by UITabBarController.
If you do mean UITabBarController then yes a UIViewController presented by a UITabBarController can very easily determine whether it is in the more options or not.
[self.tabBarController.moreNavigationController.viewControllers containsObject:self];
This will return a BOOL that is true if the option for that view controller (self) is listed in the More section of the UITabBar.
You could subclass the UIView and add a property sentFrom which you would pass along to the UIView when adding it in whatever controller. There is not an already implemented method that does it, so you have to keep track of such things by yourself.
No, UITabbarController doesn't expose that information.
NOTE:
Before reading this question please note that I have read the previous questions that explain the deficiencies regarding apple's implementation of UISplitViewController and how I should use the open-sourced "MGSplitViewController" because its not too easy to simply hide the master view controller on a split view controller in landscape-mode. Please keep in my mind that I'm limited to using the normal UISplitViewController in iOS 5.1.
Now onto the question:
I have a split view controller with table views on the left side (master view) and a detail view controller on the right. I'm using a navigation controller to control the left side which is a table view that transitions onto another table view ("DataTableViewController"). In order to hide this left side, I have placed a "hide" button on the navigation tool bar of the detail view controller. When the hide button is pressed, I change my "_hideMaster" property:
-(IBAction)hidePressed
{
_hideMaster = !_hideMaster;
// Must manually reset the delegate back to self in order to force call "shouldHideViewController"
self.splitViewController.delegate = nil;
self.spliteViewController.delegate = self;
}
and then automatically this method is called in the SplitViewController delegate:
// This is called when I change the delegate from nil back to self.
- (BOOL)splitViewController: (UISplitViewController*)svc shouldHideViewController: (UIViewController *)vc inOrientation:(UIInterfaceOrientation)orientation
{
return _hideMaster;
}
When I debug it, I can see that everything goes according to plan and the property has the correct value when it enters the method splitViewController:shouldHideViewController:inOrientation:
The only problem is that nothing happens. My left most table view (DataTableViewController) does not disappear. When I look closer, the (UIViewController *)vc parameter in the delegate method is not the table view controller that I want to hide but instead the navigation controller associated with this table view. So essentially it is trying to hide the navigation controller - which is clearly not what I want...
How can I make it so that the UIViewController parameter in the automatically called delegate method (shouldHideViewController:) calls the topmost view controller associated with that navigation controller? (After all, I want to hide DataTableViewController)
Here's how I handle it. Might need more work for making the MasterViewController reappear if it is not instantiated on the way back.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.30f];
[[self.splitViewController.viewControllers lastObject] view].frame = self.splitViewController.view.frame;
[UIView commitAnimations];
This is the first time I'm trying to implement navigation from a tableView cell to another tableView using UINavigationController and it doesn't work for me.
I'm NOT using nib file and I have a simple tableView that I present it in a modal dialog in my app, it works fine, now I added the disclosureInidcator to one of it's cell, to make the user enable to choose from a fixed number of options available from another list(tableView). For this purpose I have another class that makes the second tableView. the problem is now navigation from the cell(contains disclosure icon)in first tableview to second tableView doesn't do anything, no error, no nothing. I guess the way I setup the navigation controller would be wrong, the code doesn't fall in delegate, or datasource of the second class at all.
in First TableView in method : didSelectRowAtIndexPath I tried to catch that row, then call the second tableView like this:
mySecondViewController *secondVC = [[[mySecondViewController alloc] initWithStyle:UITableViewStyleGrouped ] autorelease];
UINavigationController *navCont = [[UINavigationController alloc] initWithRootViewController: self];//not sure the first controller should act as the root controller?
[navCont pushViewController:secondVC animated:YES]; //it does nothing, no error,...
the second tableViewcontroller class contains all delegate and datasource methods, and initialization method:
- (id)initWithStyle:(UITableViewStyle)style
{
if ((self = [super initWithStyle:style])) {
}
return self;
}
and declared in interface as:
#interface stockOptionViewController : UITableViewController {
}
I tried to play with viewDidLoad, but didn't help.
Please help me cause I have no clue and all sample codes found is based on using nib files.
Thank,
Kam
Your navigation controller should be the root view controller of the app delegate's window, and the first view controller should be the root view controller of the navigation controller, then you can push new controllers onto it.
Please see the documentation for UINavigationController.
At the moment, you are creating a navigation controller but not putting it anywhere, so asking it to push new view controllers is a little pointless. You have the right code, just not in the right order.
You can present view control modally without nav controller
mySecondViewController *secondVC = [[[mySecondViewController alloc] initWithStyle:UITableViewStyleGrouped ] autorelease];
[self presentModalViewController:secondVC animated:YES];
UINavigationController should be the root view controller. In the current code, navCont is not on the view stack, so it won't work. Instead of pushing myFirstViewController in your appDelegate, push the UINavigationController to the stack and add myFirstViewController as its root view controller.