I have an existing navigation controller delegate that places a menu button on each view controller in the app.
class MyNavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let navItem = viewController.navigationItem
let menuBtn = MyCustomMenuButton()
...
navItem.setRightBarButton(menuBtn, animated: false)
}
This works great...I get a menu button in the nav bar for each view. But for some views, I'd like to add another button on the right next to the menu button, so I added this:
class CustomViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let newButton = UIBarButtonItem(title: "(+)", style: .plain, target: nil, action: nil)
self.navigationItem.rightBarButtonItem = newButton
}
}
But this has no effect. The menu button is still there but the new button is not added. How should this be done then?
But this has no effect
Yes, it does. But you don't have time to see the effect. You are saying the same thing twice, because setRightBarButtonItem is the same as rightBarButtonItem =... So whichever one you say second, that is the one that is ultimately obeyed; either way, it rips the existing right bar button item out and replaces it with the other one.
If the goal is to have multiple right bar button items, that is what the rightBarButtonItems is for (notice the plural). You can call setRightBarButtonItems instead (again, notice the plural). Looking to see whether there is already a right bar button item and taking that into account is up to you, of course. There is no magical append method.
Related
Very stuck on an issue
I have a tab bar controller within a navigation controller
The first tab has a calendar on it (which is basically a collection view)
I am trying to make a rightbarbuttonitem to scroll to todays date
I can only seem to create the button within the tabbarcontroller
The function that I call then calls one in the CalendarViewController
but it doesn't seem to work
What is the correct way to implement a bar button item within a tabbarcontroller?
In the tabbarcontroller I have...
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .bookmarks, target: self, action: #selector(goToToday))
#objc func goToToday() {
print("pressed")
CalendarViewController().goToToday()
}
In the CalendarViewController I have...
#objc func goToToday(_ animate: Bool = true) {
print("tapped")
calendarView.scrollToDate(Date(),animateScroll: animate)
calendarView.selectDates([Date()])
title = "Test"
}
The function works if I call it from a button in CalendarViewController. And I get both print commands when called from the navigation controller but that's all
You should call gotToToday on the instance you're using as the first tab and not on a new instance as you've done here. Here's what you need to do in gotToToday in TabBarController:
#objc func goToToday() {
print("pressed")
if let calendarViewController = viewControllers[0] as? CalendarViewController {
calendarViewController.goToToday()
}
}
I have multiple view controllers embedded inside a navigation controller. I have an option that changes the language of the user interface. When user chooses French, the user interface should update itself with French language. All elements are updated with French as I expected, except the button that takes user back to the previous view controller (as you can see in the screenshot - "List of Events").
This is the function I call to update UI :
func updateView() {
DispatchQueue.main.async {
// CurrentSelection.LanguageUI holds a reference to currenly selected language object
navigationItem.title = CurrentSelection.languageUI.event_singular
navigationController?.title = CurrentSelection.languageUI.listOfEvents <<- this line doesn't work
}
}
The navigationController?.title holds "List of Events" string and the assignment statement seems to work. It just that the UI isn't updated with the new title value. Where am I doing wrong?
It's the previousVC that decides what it's own back button will be.
So set this on the previousVC in viewDidLoad
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back title" style:UIBarButtonItemStylePlain target:nil action:nil];
If this isn't set then the back button will be the .title of the previous VC.
-
In your situation you should have some kind of language change notification that the previous VC can listen for and know to update its own title.
One way to do it is to implement UINavigationControllerDelegate and set the back bar button item there:
class YourViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
// MARK: - Navigation controller delegate
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let item = UIBarButtonItem(title: "Custom title", style: .plain, target: nil, action: nil)
viewController.navigationItem.backBarButtonItem = item
}
}
Something to be aware of is that this needs to be implemented in the view controller which pushes the next view controller. So you'll be changing the back bar button item of the view controller that willShow.
Those view controllers in the navigation stack won't change atomatically.
So you have to use Notification center and add observers to the previous view controllers and change the Title of the navigation bar with selected language when it is called.
When language is changed you have to post the change.
Then it will change the title to french so does the back button will
change.
I am building a mobile app which displays stats from multiple APIs.
The app shows different values based on the API that is active. At runtime, the user can select which API to display values for. However, the values are always displayed within the same Target view controller.
UINavigationController is in use and I'd like to defer to that for navigation and navigation UI, if possible, even though UITableViewControllers support navigation bars.
Please see the storyboard design image at the end of the question.
Environment: iOS 9/Swift 2.2/XCode 7
At runtime, the user will select a Choice view controller from the Select controller. What is the appropriate way to segue from one of the Choice view controllers, via a "Done" UIBarButton, to the Target controller? The Target controller does not change.
Must I programmatically define the right bar button to "Done" for every controller that is embedded within the UINavigationController?
class UIViewController: UITableViewController {
override func viewWillAppear( animated: Bool) {
super.viewWillAppear( animated)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: nil)
}
}
The proper way to return to the root view controller is:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(MyViewController.done))
}
func done() {
if let navigationController = self.navigationController {
navigationController.popToRootViewController(animated: true)
}
}
}
You do need to programmatically add a "done" button; but it's more simple than that. Just select your root view controller, and copy/past the class in storyboard. Use your segues to push the correct view onto the UINavigation stack.
This question already has answers here:
Setting action for back button in navigation controller
(29 answers)
Closed 7 years ago.
I want to add an Action to my standard Back-Button, but this won't work as I found from below link:
add-target-to-stock-back-button-in-navigationbar
So I created a custom button with an action.
UIBarButtonItem(title: "❮ Back", style: .Bordered, target: self, action: "back:")
But how can I set the standard left wing to the title?
The character '❮' is way smaller than that from the standard button. I use the standard button throughout my app, and I want to keep this optic.
If you want to "add" some functionality to your back button you can use the UINavigationControllerDelegate.
When you press the back button, the navigation controller will call navigationController(willShowViewController:) so you can use this to do whatever you want to do. An example is shown below:
Using UINavigationControllerDelegate:
class ViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
// Add whatever you want to do here
}
}
I have a question regarding the navigation bar.
As far as I understand from iOS: A view controller opened by a segue inherits the navigation bar of the parent view controller. Is this correct so far?
Is there a view controller within a stack "owns" the navigation bar in a complex segue stack (e.g. TableViewController that opens a TabBarController that opens ...)?
I very often run into the problem that I don't know where to get the actual navigation item in order to set the title or a bar button item.
In this case, I have the following controllers:
TabBarController
EventPostsViewController -> To display a list of posts, is a tabbed view within the TabBarController
CreatePostViewController -> To write a new post
So within the EventPostsViewController I can do this (and it works):
class EventPostsViewController: UITableViewController {
...
override func viewWillAppear(animated: Bool) {
...
// This solution works, but only for EventPostsViewController
self.tabBarController?.navigationItem.title = "text"
But within the CreatePostViewController, which is opened by a segue via EventPostsViewController, neither of this solutions work.
class CreatePostViewController: UIViewController {
...
override func viewWillAppear(animated: Bool) {
...
// Neither of these solutions works
self.navigationItem.title = "Text"
self.tabBarController?.navigationItem.title = "Text"
self.navigationController?.navigationItem.title = "Text"
How do I get the actual navigation bar/navigationItem?
Stupid simple mistake I repeat every time :)
I forgot to link my custom CreatePostViewController with the view controller using the interface builder.
This code now works:
class CreatePostViewController: UIViewController {
...
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
self.navigationController?.setNavigationBarHidden(false, animated: false)
// Set title
self.navigationItem.title = "Write Post"
// Add Submit button
var submitButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: "submitPost:")
self.navigationItem.rightBarButtonItem = submitButton
}
...
}