Displaying an UIViewController modally from one of the View Controllers(which are in a Navigation controller)
code :
self.present(viewController, animated: true, completion: nil)
Within the modal ViewController, a custom Navigation Bar along with navigation item is added. In which, i'm trying to display the Back Button.
let backButton = UIBarButtonItem()
backButton.title = "Back"
self.navigItem.backBarButtonItem = backButton //self.navigItem is the custom Navigation Item
Is there something im missing here?
thanks in advance,
Rajesh
let btnleft : UIButton = UIButton(frame: CGRect(x:0, y:0, width:35,
height:35))
btnleft.contentMode = .center
btnleft.setImage(Set_Local_Image("arrow_left"), for: .normal)
btnleft.addTarget(self, action: #selector(YOUR_ACTION), for: .touchDown)
let backBarButon: UIBarButtonItem = UIBarButtonItem(customView: btnleft)
self.navigationItem.setLeftBarButtonItems([menuBarButon], animated:false)
In Swift3 try this code on the viewDidLoad
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "arrow_left")
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "arrow_left")
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -60), for:UIBarMetrics.default)
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationItem.hidesBackButton = false
Related
am using navigation bar programmatically in swift, but am not able to show the bar button items in navigation bar,
this is the code what I did
override func viewDidLoad() {
super.viewDidLoad()
let navBar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: 420, height: 65))
self.view.addSubview(navBar)
navBar.backgroundColor = hexStringToUIColor("4DC8BD")
let navigationItem = UINavigationItem()
self.title = "Transport APP"
let btn1 = UIButton(type: .custom)
btn1.setImage(UIImage(named: "Menu1"), for: .normal)
btn1.frame = CGRect(x: 30, y: 30, width: 30, height: 30)
btn1.addTarget(self, action: #selector(HomeViewController.menubuttonclick(_:)), for: .touchUpInside)
let item1 = UIBarButtonItem(customView: btn1)
self.navigationItem.setRightBarButtonItems([item1], animated: true)
}
#IBAction func menubuttonclick(_ sender:UIBarButtonItem )
{
print("this menu button click")
}
I can try many ways but am not getting the results
how to show show bar button item in navigation bar,
You should add UINavigationItem to your UINavigationBar and in item1 need to be added in navitem Look at below code
let navitem = UINavigationItem()
navitem.rightBarButtonItem = item1
navBar.setItems([navitem], animated: true)
Swift 3+: Define the barbutton.
//:: Left bar items
lazy var leftBarItem: Array = { () -> [UIBarButtonItem] in
let btnBack = UIButton(type: .custom)
btnBack.frame = kBAR_FRAME
btnBack.addTarget(self, action: #selector(clickOnBackBtn(_:)), for: .touchUpInside)
let item = UIBarButtonItem(customView: btnBack)
item.tag = 3
return [item]
}()
Add this line into viewDidLoad
self.navigationItem.setLeftBarButtonItems(self.leftBarItem, animated: true)
Bar Button Action
#objc func clickOnBackBtn(_ sender: Any){
}
While the other mentioned solutions definitely work for programmatically defining the navigation item, some would prefer a storyboard solution. I searched for a Swift 4, Xcode 9 storyboard solution and was unable to find one, so I will show my solution.
Here is a screenshot of my storyboard before adding the bar button item.
The issue I was having is that while the Shops tableview is embedded in the navigation controller, and adding a bar button item was no issue; the Employees tableview is pushed via the navigation controller in the didSelectRowAt function.
extension ShopsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedShop = fetchedResultsController.object(at: indexPath)
let st = UIStoryboardname: "Main", bundle: Bundle.main)
let vc = st.instantiateViewController(withIdentifier: "EmployeeViewController") as! EmployeeViewController
vc.shop = selectedShop
self.navigationController?.pushViewController(vc, animated: true)
}
}
The result is that I could not drag a bar button item from the storyboard. When I tried, the item would end up in the tab bar at the bottom:
I found an article that suggested embedding the second view controller in a navigation controller, but that adds other levels of complexity that I wanted to avoid. The work around I found is to drag a navigation item to the navigation bar area, and then you can add a bar button item with no problems.
Here is the result:
I know there is a lot of debate whether storyboards or programmatic layout is better. While I am still very much a beginning iOS developer and cannot personally speak to that, I am finding that sometimes the storyboard solution fits the problem best. I hope this helps other beginners.
if you've already created UINavigationController() you can set navigation items using self.navigationItem like this (in viewDidLoad function):
self.navigationItem.title = "Transport APP"
self.navigationItem.rightBarButtonItem = item1
but if you need to know how to create UINavigationController() for a view, you can do this in AppDelegate class:
let myView = ... //initial your view controller
let nav = UINavigationController()
nav.viewControlles = [myView]
and then everywhere you need, you should push nav view.
let rightBarButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(dismissVC))
self.navigationItem.rightBarButtonItem = rightBarButton
I've watched a lot of questions like this and didn't find an answer for my question.
That how i do now:
APPDELEGATE (didFinishLaunchingWithOptions)
// Text
let barButtonItem = UIBarButtonItem.appearance()
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: UIControlState.normal)
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: UIControlState.highlighted)
// Image
let backImage = UIImage(named: "arrow_left"
UINavigationBar.appearance().backIndicatorImage = backImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backImage
And this almost fit to what i need, but the screen title shifted to the right as there is an invisible back button text. And it definetly is (the root controller's title has 9 characters length):
The question is: How to change image, hide text and keep standart back action for every appearance of back button in ios 9.0 ?
There are three ways to do what you want.
I recommend: Create your own Navigation Class by extending and UINavigationController and override backbuttonItem (UIBarButtonItem) property to customise it according to your requirement. And use the same Navigation Controller class in your project.
Create a custom backBarButton by extending UIBarButtonItem and manually set the same as a back button of default Navigation Controller class, in all view controller.
Hide default navigation bar from root controller and create your own navigation bar using UIView and UIButton in all view controllers. (I always use this choice, that makes customization of navigation bar very easy for me. And I can set my view according to my requirement)
Here is how you can add Custom button for your navigation bar
let btnleft : UIButton = UIButton(frame: CGRect(x:0, y:0, width:35, height:35))
btnleft.contentMode = .center
btnleft.setImage(Set_Local_Image("arrow_left"), for: .normal)
btnleft.addTarget(self, action: #selector(YOUR_ACTION), for: .touchDown)
let backBarButon: UIBarButtonItem = UIBarButtonItem(customView: btnleft)
self.navigationItem.setLeftBarButtonItems([menuBarButon], animated:false)
instead of "arrow_left" You can use any image you want
For Default back action you can create function(YOUR_ACTION) and use in selector of back button
navController.popViewController(animated: true)
I can suggest you 2 options. Both require BaseViewController class as a superclass of all your view controllers.
If you are ok with native back button image, just want to remove back button text you can use this subclass:
class BaseViewController: UIViewController {
var navigationTitle: String = ""
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if !navigationTitle.isEmpty {
navigationItem.title = navigationTitle
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationTitle = navigationItem.title ?? ""
navigationItem.title = ""
}
}
If you want to use your custom icon for back button, you should create UIBarButtonItem with your image, add target, selector, handle action of the button. Sample BaseViewController class below:
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let backImage = UIImage(named: "arrow_left")
navigationItem.hidesBackButton = true
guard let navigationController = navigationController else {
return
}
if navigationController.viewControllers.count > 1 {
// we have to set back button only when we have at least 1 controller to go back
navigationItem.leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(backBarButtonAction(sender:)))
}
}
// MARK: Actions
func backBarButtonAction(sender: UIBarButtonItem) {
navigationController?.popViewController(animated: true)
}
}
According to https://stackoverflow.com/a/16831482/5790492 there is a way to do this without appearance.
Swift 3.0
extension UIViewController {
func setupCustomBackButton() {
if let controllersCount = navigationController?.viewControllers.count, controllersCount > 1 {
let backButton = UIButton(frame: CGRect(x: 0, y: 0, width: 12, height: 20))
backButton.setBackgroundImage(UIImage(named: "arrow_left"), for: .normal)
backButton.contentMode = .left
let backButtonItem = UIBarButtonItem(customView: backButton)
backButton.addTarget(self, action: #selector(self.popCurrentViewController), for: .touchUpInside)
navigationItem.leftBarButtonItem = backButtonItem
navigationItem.hidesBackButton = true
}
}
func popCurrentViewController() {
navigationController?.popViewController(animated: true)
}
}
UINavigationBar.appearance().setBackgroundImage(UIImage(), for:
UIBarPosition.any, barMetrics: UIBarMetrics.default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().barTintColor = UIColor.main
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().clipsToBounds = false
UINavigationBar.appearance().backgroundColor = UIColor.main
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName :
(UIFont(name: "Helvetica", size: 18))!, NSForegroundColorAttributeName:
UIColor.white]
Try this code and make changes accordingly to set image, color and other properties
You should watch Mark Moeykens' youtube series on this. He is IMHO one of the best YouTube presenters for UI Design and implementation in Swift.
The play list is https://www.youtube.com/playlist?list=PLHDMmeIMXj8WyvlX5uFmppVn2Pm0bXVr7
Create an extension for UINavigationItem
extension UINavigationItem {
func backBarButtonItem() -> UINavigationItem {
return UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
}
}
I have two viewControllers.
vc1 -> presentVC -> vc2
vc2 inherit UINavigationController
I want to set title & backButton in vc2,but it doesn't work.
class vc2: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
// set title!!!
//self.navigationItem.title = "123"
//self.navigationController?.navigationBar.topItem?.title = "123"
//self.title = "123"
//self.navigationBar.topItem?.title = "123"
//self.navigationItem.title = "123"
// set backButton!!!
let navButtonWidth:CGFloat = 44
let backButton:UIButton = UIButton()
backButton.setImage(backImage, for: .normal)
backButton.addTarget(self, action: #selector(back), for: .touchUpInside)
self.navigationItem.leftBarButtonItems = [UIBarButtonItem(customView: backButton)]
}
Place the code below in perform(segue) or viewWillDissappear depended on how do you do your presentation - via Storyboard segue or manually from code.
let backButton = UIBarButtonItem()
backButton.title = "whatever_you_want"
navigationItem.backBarButtonItem = backButton
And in viewDidLoad of your vc2 simply put
navigationItem.title = "Controller title"
I can change it in storyboard Or change it in code.
I usually do this.
First hide navigation bar from default navigation controller.
navigationController?.navigationBar.hidden = true
Create navigation bar in storyboard and outlet it.
#IBOutlet weak var navigationBar: UINavigationBar!
Create custom navigation item.
private lazy var customNavigationItem: UINavigationItem = {
let navigationItem = UINavigationItem()
let backButton = UIBarButtonItem(image: UIImage(named: "cancel_icon"), style: .Plain, target: self, action: #selector(cancelTapped))
navigationItem.leftBarButtonItem = backButton
navigationItem.title = "Your Title"
return navigationItem
}()
Add custom navigation item to navigation bar
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.hidden = true
navigationBar.setItems([customNavigationItem], animated: false)
}
Hope it help.
I'm using JSQmessageViewController and trying to programmatically add "back" button & user image on the navigation bar. I'm using the following codes. After running it, there's no "back" button or image. Attached is the screenshot of the simulator. I tested these codes with the normal UIViewController, and they worked.
May I know why they don't work with the JSQmessageViewController? And what should I do to add the "back" button & image on the navigation bar? Thanks a lot!
let navigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.frame.size.width, 64))
navigationBar.backgroundColor = UIColor.whiteColor()
navigationBar.delegate = self;
let navigationItem = UINavigationItem()
navigationItem.title = strValue
let leftButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "back:")
self.navigationItem.leftBarButtonItem = leftButton
let imgButton = UIButton()
imgButton.setImage(image, forState: .Normal)
imgButton.addTarget(self, action: "EditProfile:", forControlEvents: UIControlEvents.TouchDown)
imgButton.frame = CGRectMake(self.view.frame.width - 60, 0, 41, self.view.frame.height)
var rightButton = UIBarButtonItem(customView: imgButton)
self.navigationItem.rightBarButtonItem = rightButton
navigationBar.items = [navigationItem]
self.view.addSubview(navigationBar)
}
So if you use a navigation controller to present your instantiation of the JSQMessagesViewController then the navigation bar should actually already be there. You can just provide it with those buttons and they will be in the correct place. It also seems that you may be doing things in an outdated syntax. Here is the latest Syntax.
Create a function to add your back button.
func addCancelButtonLeft() {
let button = UIBarButtonItem(barButtonSystemItem: .back, target: self, action: #selector(dismissView))
navigationItem.leftBarButtonItem = button
}
Create the action for the button.
func dismissView() {
dismiss(animated: true, completion: nil)
}
Then for your Image you are actually trying to put a button in that title view. Which is fine.
func titleView() {
let imgButton = UIButton()
imgButton.setImage(image, forState: .Normal)
imgButton.addTarget(self, action: #selector(EditProfile), forControlEvents: .touchUpInside)
navigationItem.titleView = imgButton
}
func EditProfile() {
navigationController?.present(EditProfileViewController(), animated: true, completion: nil)
}
let me know if you have more questions and I will see what I can do. Good luck 👍🏼
I have a view who is called from a Tab Bar controller where i want to have a leftBarButtonItem, a topItem with the logo and a rightBarButtonItem.
On this first view i succeeded to have the topItem with the logo and the rightBarButtonItem, but impossible to have the leftBarButtonItem, programmatically or dragging it in the storyboard.
And i don't understand why. Here is the code.
private func setNavBarItems() {
self.navigationItem.leftBarButtonItem?.image = UIImage(named: "empty-photo-red")
if let navigationBar = self.navigationController?.navigationBar as? SeetyNavigationBar {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "logo-trans")
imageView.image = image
navigationBar.topItem?.titleView = imageView
}
self.navigationItem.rightBarButtonItem!.image = UIImage(named: "FAQ")
}
If i unwrap leftBarButton with "!" the app crash, so i guess there is no leftBarButton, but why ?
And after when i perform the segue from this view to the next one, i got the leftBarButtonItem and the rightBarButtonItem and the topItem with the logo disappear. I use the same function that i call in my viewDidLoad()
EDIT: For my topItem who was disappearing: self.navigationItem.titleView = imageView
and not navigationBar.topItem?.titleView = imageView solved the problem.
It appears that you did not created any button, before setting the image you have to create the button.
Also for the image that disappear after going the the second screen there is two options, you have your tab bar controller, beneath it you have two views which are linked to your tab bar controller, from there :
you embedded the tab bar controller in a navigation controller (which is not the way to go btw), so to put your buttons, you have to put'em on the parent controller like this for example
// adding right button
let changeLocButton = UIBarButtonItem()
changeLocButton.title = "/!\\"
changeLocButton.action = "AnnotationsStatus:"
changeLocButton.target = self
self.parentViewController?.navigationItem.setRightBarButtonItem(changeLocButton, animated: false)
you embedded each of your two views inside a navigation controller, so you have to create the buttons for EACH view.
// adding right button
let changeLocButton = UIBarButtonItem()
changeLocButton.title = "/!\\"
changeLocButton.action = "AnnotationsStatus:"
changeLocButton.target = self
self.navigationItem.setRightBarButtonItem(changeLocButton, animated: false)
for your image it's the same
- case 1 :
if let navigationBar = self.parentViewController?navigationController?.navigationBar as? SeetyNavigationBar
-case 2 : just copy paste the code with navigation bar in the second view controller
Swift 4.0
let btnBack = UIButton(type: .custom)
btnBack.bounds = CGRect(x: 0, y: 0, width: 44, height: 44)
btnBack.addTarget(self, action: #selector(self.backButtonAction(sender:)), for: .touchUpInside)
btnBack.setImage(#imageLiteral(resourceName: "back_image"), for: .normal)
let leftBarButton = UIBarButtonItem(customView: btnBack)
self.navigationItem.setLeftBarButton(leftBarButton, animated: true)
// Back Button Action
#objc func backButtonAction(sender: UIButton) {
self.navigationController?.popViewController(animated: true)
}