Please forgive me, there are already a ton of questions on how to add a UIBarButtonItem to a NavigationBar programmatically but I just can't seem to get any of the solutions to work in my situation.
Here is the layout of a simple test app I have been working with to learn the UIPageViewController.
I have the page view controller working nicely with three unique viewControllers. I would now like to set unique rightBarButtonItems for each of the view controllers. I can easily set a common barButtonItem in the DetailViewController by simply doing this.
UIBarButtonItem *newButton = [[UIBarButtonItem alloc] initWithTitle:#"Whatever"
style:UIBarButtonItemStylePlain
target:self
action:#selector(doSomething)];
self.navigationItem.rightBarButtonItem = newButton;
Given that I need a unique button for each of the three pages in the pageViewController I am not able to set the button in the detailViewController. I have tried the code above in viewDidAppear of the three controllers but the buttons will not appear. I have also tried creating the button in the same way and then setting it like this with no luck.
DetailViewController *vc = [[DetailViewController alloc] init];
vc.navigationItem.rightBarButtonItem = newButton;
I know I'm close here, I'm just not sure how to specify I need to add a button to the NavigationBar of the NavigationController that is running the detailViewController that my contentViewControllers are embedded in. Any help would be great ESPECIALLY SNIPPETS.
As a side note I have tried a few other methods that have come up problematic.
Setting the button in viewDidLoad rather than viewDidAppear
This will not work because the viewDidLoad method is not called everytime you swipe from one page to the next. The viewDidAppear method is however so I am trying to set the button there. It is possible viewWillAppear is called everytime but I haven't checked.
Setting the button in the UIPageViewController delegate methods
This is problematic if the user flips through the pages to quickly the button will fail to change or fail to appear.
SOLUTION
It turns out I was close… Rather than creating the button in the UIPageViewController methods I simply needed to create a navigationItem property in each of my three view controllers. When the controllers are instantiated in the delegate methods I simply set the navigationItem property equal to that of the detailViewController. That way in my viewDidApper methods I could create the button. Then at viewWillDisappear I set the button to nil. You can also just create the button at viewdidLoad of the DetailViewController and change the text and action in viewDidAppear of the individual viewControllers. Here is a sample.
In the delegate methods
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
I call a helper method based on the index of the viewController. The helper method instantiates the view controllers when it is called. Once instantiated I set some properties including the navigationItem property.
-(FirstController *)controllerAtIndex:(NSUInteger)index
{
FirstController *fvc = [self.storyboard instantiateViewControllerWithIdentifier:#"FirstPageController"];
fvc.imageFile = self.pageImages[index];
fvc.titleText = self.pageTitles[index];
fvc.pageIndex = index;
fvc.navItem = self.navigationItem;
return fvc;
}
Then in the viewDidAppear of the viewController I just do this
-(void)viewDidAppear:(BOOL)animated
{
UIBarButtonItem *newButton = [[UIBarButtonItem alloc]
initWithTitle:#"Whatever" style:UIBarButtonItemStylePlain target:self action:#selector(doSomething)];
navItem.rightBarButtonItem = newButton;
}
If you need the button to change each time you change the page you look at, it must be done in one of the delegate methods that you have tried. I suggest however, that instead of creating a new button and setting it to the navigation item, you simply change the title.
You can create the button in interface builder and link it to a property in your DetailViewController and then call setTitle:forState with UIControlStateNormal on the button every time you go to a new page.
If you need the button to do something different for each page, I would recommend checking the current page in the buttons action method rather than declaring a new one each time.
You can find the correct navigation item to set buttons/titles on by finding the page controller in one of its paged view controllers, then getting its nav item. e.g.
UIViewController *pageController = [[self.navigationController childViewControllers] lastObject];
pageController.navigationItem.title = #"My page's title";
It's really annoying that self.navigationItem doesn't work!
One of my colleagues experienced the same problem with page view controller.
As far as I understand, you wont' be able to add UIBarButtonItem to navigation bar of member view controllers in UIPageViewController. Reason being 'navigationController' is set to nil for UIPageViewController. To confirm, print value of [UIPageViewController navigationController].
However you can do one thing to overcome this issue. Please download example PhotoScroller.
And do following changes in AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// kick things off by making the first page
PhotoViewController *pageZero = [PhotoViewController photoViewControllerForPageIndex:0];
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:pageZero];
if (pageZero != nil)
{
// assign the first page to the pageViewController (our rootViewController)
UIPageViewController *pageViewController = (UIPageViewController *)self.window.rootViewController;
pageViewController.dataSource = self;
[pageViewController setViewControllers:#[nav]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
}
return YES;
}
And following changes in the PhotoViewController.m
- (void)loadView
{
.....
.....
self.title =#"Frogs";
UIBarButtonItem *newButton = [[UIBarButtonItem alloc] initWithTitle:#"Something" style:UIBarButtonItemStylePlain target:self action:#selector(doSomething)];
[newButton setTintColor:[UIColor redColor]];
self.navigationItem.rightBarButtonItem = newButton;
}
To avoid crash, you have to handle UIPageViewControllerDataSource delegate methods correctly in AppDelegate.m
Though, I wouldn't advice you to do above as it breaks the whole concept of UIPageViewController.
I hope this would be helpful.
Related
I got a NavigationController(short for nv) and a ViewController(short for vc).
nv's RootViewController is vc.
My Goal
I just want to set the NavigationItem , I want the left bar button to be BACK button(customized).since this left button shared in all ViewControllers so I don't want to write the code in the ViewController , otherwise I have to write it many times. And I can't just subclass a ViewController, since I only need my customized left bar item in just a few ViewController.
So I try adding the generating button code in my nv , but i failed.
The result is , only the original vc got customized left bar button. other vc which is push on it all got the default one.
My Code
Here is my NavigationController's code:
- (void) viewWillAppear:(BOOL)animated{
[self createBackButton];
- (void) createBackButton{
UIViewController* vc = [self.viewControllers lastObject];
vc.navigationItem.leftBarButtonItem = nil;
vc.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc]initWithTitle:#"Roll Back"
style:UIBarButtonItemStyleBordered
target:self
action:backButtonPressedSelector];
}
But if I put my createBackButton method in my ViewController , everything works just fine. But I also Have to write this method many times. Even the sentence like [self createBackButton]
it seems that viewWillAppear isn't a good time to call my createBackButtonMethod
My Question
In what order does the method below be called when I am pushing and hoping view controller , and where should I add my createBackButtonMethod in order to got some of the ViewController have the same left bar button without writing unnecessary code?
1、NavigationController's didload
2、NavigationController's willappear
3、ViewController(the one being pushed) 's didload
4、ViewController's willappear
Answer
The the method calling order is this:
1、vc's didload
2、nv's didload
3、nv's will appear
4、vc's will appear
and then when I continue to push vc, there are only
vc's didload and vc'didload being called repeatedly.
That's why I failed, because the nv's didload will only be called once here.
Instead of writing a common parent class, I think of the pretty solution below.
just do the adding things in the pushingViewController:method
So I override my pushViewController: method
- (void) pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
viewController.navigationItem.leftBarButtonItem = nil;
viewController.navigationItem.leftBarButtonItem =
[[UIBarButtonItem alloc]initWithTitle:#"Roll Back"
style:UIBarButtonItemStyleBordered
target:self
action:backButtonPressedSelector];
}
try to make parent view controller and extend all your view controllers from it and add the needed code in there all the view controller will have it
hope i understand your question right and this answer help you with your problem good luck
--> Put all your code in viewcontroller's viewdidload or viewwillappear.
-->Viewdidload calls only once when you push the controller and viewwillappear will call every time in any case push, pop, present, dismiss etc.
--> First will call viewdidload and then will call view will appear.
Note: Problem solved.
Here comes the story. I am using RevealViewController in my project. I am also hiding the navigationBars:
[self.navigationController setNavigationBarHidden];
My project can be seen in the picture below and the "menuButton" is implemented in the tabBarViewController.
Since the navigationBar is hidden due to my interface looks, all tabViews (HomeViewController) will not show the menuButton and the navigationBar as supposed to. I am not using panGestureRecognizer to trigger the menu aswell.
This means I have a problem to trigger the menu via a normal button in HomeViewController. The menuButton-event is placed in tabBarViewController.m:
_menuButton.target = self.revealViewController;
_menuButton.action = #selector(revealToggle:);
So I tried to call a method from HomeViewController to fire the button in tabBarViewController like this:
HomeViewController.m
- (IBAction) onMenuClicked: (id)sender{
tabBar = [[tabBarViewController alloc] init];
[tabBar setupMenu]:
}
tabBarViewController.m
-(void) setupMenu{
[_realMenuButton sendActionForControlEvents:UIControlEventTouchUpInside];
[_realMenuButton addTarget:self.revealViewController action:#selector(revealToggle:) UIControlEventTouchUpInside];
}
In this example I tried to make the realMenuButton and normal UIButton. Ive also tried as a UIBarButtonItem just to trigger the #selector(revealToggle:) But nothing happens in the app when I try to trigger the button from HomeViewController.
Not sure how I shall make this work. Any other Ideas or tricks? Please be specific if so! Regards
Yes, it will still work.
SWRevealViewController is just a subclass of a UIViewController, so you can use it at any point in the app:
By calling presentViewController:animated at some point.
By using it in a navigation stack etc.
Note that you can add gestures from SWRevealViewController to its content view controllers, which will alter the behaviour of used in a navigation view controller, but that's to be expected, and you still have full control over its behaviour.
Edit
The UI structure of your app is still not clear to me - it looks like you're trying to call revealToggle on an instance of SWRevealViewController when the VC in view is infact HomeViewController? How would this work, when SWVC is not even in view?
My best guess is that your UI structure should be as follows:
TabBarController --->(root)UINavigationController --->(root)SWRevealViewController.
Then, on your SWRevealViewController, set HomeViewController as the front view controller, and the TableViewController as the right or left view controller.
Do you mean like this?
it is possible. you can set the menu button in your tabBarController.m, like this :
UIBarButtonItem *menu = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:#"menu.png"] style:UIBarButtonItemStylePlain target:revealController action:#selector(revealToggle:)];
self.navigationItem.leftBarButtonItem = menu;
self.delegate = self;
For me, my initial view controller is the login screen (obviously I don't need reveal any VC here...). then when user tap the login button,
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:yourRootVC];
LeftMenuViewController *leftMenuVC = [[LeftMenuViewController alloc]init];
SWRevealViewController *revealController = [[SWRevealViewController alloc]initWithRearViewController:leftMenuVC frontViewController:nav];
revealController.delegate = self;
[self presentViewController:revealController animated:YES completion:nil];
I've tried it and it should work as normal. Even it isn't initial view controller
I've looked at all other answers for this topic on Stackoverflow but don't get really further. I've set up my Tabbar controller in Storyboard. I've defined the icons for the tabbar items also in Storyboard, the titles however I've set via code in their respective view controllers since my app offers multi-language support.
Now I want one single tabbar button which doesn't segue to another view but just calls an actionsheet. No matter on which other tabbar I am. So my questions are:
Where do I add this tab bar button? Because all other buttons I can only define after creating the segue to the new view controller
Where do I place the code for the action sheet?!
I'm not very clued up on the tabbar, however i would do the following:
Assuming you are using a TabBarController View or a Central View With a TabBar in it.
In the -viewDidLoad
NSMutableArray *tmp = [[NSMutableArray alloc] initWithArray:self.tabBarController.viewControllers];
UIViewController *sheet= [[UIViewContoller alloc] init];
sheet.title = #"Sheet";
sheet.tabBarItem.image = [UIImage....];
[tmp addObject:sheet];
[self.tabBarController setviewControllers:tmp];
self.tabBarController.delegate = self;
Then place the following
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if([viewController.Title isEqualToString:#"Sheet"])
{
//ActionSheet create code here
retrun NO;
}
else
{
return YES;
}
}
I have a NavigationController then a TabBarController which has Four Tabs.
I wanted to display Different titles on TopBar when a Different Tab is selected.
One way was to Embed each TabBarItem View into Navigation Controller but for some reason this doesn't seems the correct way, i wanted to apply this via code.
I managed accomplish this by using this code: (Products_ViewController.m custom class)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UINavigationController *navCon = (UINavigationController*) [self.navigationController.viewControllers objectAtIndex:0];
navCon.navigationItem.title = #"Products";
}
But the problem is now when a tab is clicked First time, it changes the title but then it doesn't. I then applied the same code on -(void)viewDidAppear{} but still the same result.
How can i manage to display navigation top bar title (or run the above code) whenever the tab bar item is clicked or the view is shown?
Thanks!
You could implement the UITabBarControllerDelegate in the Products_ViewController.m class and execute your code in the tabBarController:didSelectViewController: method.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
UINavigationController *navCon = (UINavigationController*) [self.navigationController.viewControllers objectAtIndex:0];
navCon.navigationItem.title = #"Products";
}
In the viewDidLoad method you have to set the delegate to self.
I'm trying to create a transition between two scenes, this is a dumbed down version of what I have in my production code :
Both are ViewController, the left one has a TableView inside it and when clicked it should transition to the right hand scene, passing along data from whatever cell was clicked.
Currently, with a modal segue I can tap the cell and it transitions correctly, however, I can't figure out how to place a back button onto the nav bar.
I'm transitioning from the cell to the 2nd view controller like so :
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toSecond" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toSecond"])
{
NSLog(#"Preparing segue for toSecond, setting some data on target scene");
NSIndexPath *path = [self.theTableView indexPathForSelectedRow];
MyData * myData = [myDataArray objectAtIndex:path.row];
// Obtain handles on the current and destination controllers
FirstController * startingViewController;
SecondController * destinationController;
startingViewController = (FirstController * ) segue.sourceViewController;
destinationController = (SecondController * ) segue.destinationViewController;
destinationController.someData = myData;
}
}
On the SecondController, I've tried amending the viewDidLoad method to programatically include a back button item as suggested in this previous SO question:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIBarButtonItem * back = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
[self.navigationItem setBackBarButtonItem:back];
}
So my question is, how can I get a back button onto that nav bar? Something like this :
Thanks
One (easy) way, using the storyboard, is to embed a navigation controller into your view that has the table. Make sure the correct view, the one with the table, is highlighted and then go to Editor > Embed In > Navigation Controller.
The back button will automatically be there when you click a row of your table. Like Dan said, make sure it is a push segue between the 2 views in your picture.
You will not be able to add a back looking button to a modally presented ViewController easily
If you just want to add a normal button to the left side of the bar, do the following
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonSystemItemDone target:nil action:nil];
UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:#"Title"];
item.leftBarButtonItem = rightButton;
item.hidesBackButton = YES;
[self.yourNavigationBar pushNavigationItem:item animated:NO];
If you insist of presenting your view modally and still want a back button style you could use
Three20
In place of where you have the view with the table, us a UINavigationController don't delete the view with the table, just in place of where you segue to it, or if it was the root controller, use the nav con. Make the view with the table the root view controller of the navigation controller, and then simply use a push segue instead of a modal segue, and you should automatically get the back button
The best way is to just use a UINavigationController as the parent of your table view controller, and use a push segue instead of a modal segue.
You can create two instances of UINavigationItem and tell the UINavigationBar about them by setting the bar's items property. Instance 0 represents the table view controller.
You can create a UIButton with type 101 (the undocumented back button type), and wrap it in a UIBarButtonItem using initWithCustomView:.