UINavigationBar - prefersLargeTitles - How to set image to the left of the title? - ios

The below article shows how to add an image to the right of the navigation title.
https://blog.uptech.team/how-to-build-resizing-image-in-navigation-bar-with-large-title-8ba2e8bcb840
I'd like to add the image to the left of the navigation bar title. To achieve it, I set the leftAnchor instead of the right anchor for the imageView like below.
imageView.leftAnchor.constraint(equalTo: navigationBar.leftAnchor, constant: Const.ImageRightMargin)
The image shows above the navigation bar title, which is not what I'm after. I'd like the image to be on the same line as the title (left of the title).
Note:
I've seen other questions on SO about adding images to the title
view but this question specifically requests information about
adding image to the left of the title. Therefore, this question
is not a duplicate.
I know that I can set a custom title view like below but I do not
want to lose the prefersLargeTitles setting of the navigation bar.
self.navigationItem.titleView = customTitleView
Any help is much appreciated.

import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let img = UIImage.init(named: "imgName")
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
imgView.image = img!
imgView.contentMode = .scaleAspectFit
let item = UIBarButtonItem.init(customView: imgView)
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
navigationItem.leftBarButtonItems = [negativeSpacer, item]
}
}
The result
If you want to execute a request when you click the image, replace this line
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
with that line
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: .fixedSpace, target: self, action: #selector(go))
And place this
#objc func go() {
print("go")
}

Related

Need to set logo in left side of Navigation bar in AppDelegate

I am working on a swift project.In that i need the logo to be shown in left side of navigation bar and would like to make it globally in AppDelegate. But self.navigationitem is not detected in Appdelegate?Any Help would be appreciated as its my first project in swift.
UINavigationBar.appearance().barTintColor = Constants.templatecolor
let logoImage = UIImage.init(named: "logoImage")
let logoImageView = UIImageView.init(image: logoImage)
logoImageView.frame = CGRect(x: -40, y: 0, width: 150, height: 25)
logoImageView.contentMode = .scaleAspectFit
let imageItem = UIBarButtonItem.init(customView: logoImageView)
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
negativeSpacer.width = -25
UINavigationItem.setLeftBarButtonItems(negativeSpacer, imageItem)
You can't set the Navigation item in AppDelegate. Navigation item is the property of UIViewController or UINavigationController and you set the UINavigationItem for each ViewControllers.
If all that you wanna achieve is setting the navigation title for all screens declare a base class call it as BaseViewController n in viewDidLoad of BaseViewController set the navigation item
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let logoImage = UIImage.init(named: "close")
let logoImageView = UIImageView.init(image: logoImage)
logoImageView.frame = CGRect(x: -40, y: 0, width: 150, height: 25)
logoImageView.contentMode = .scaleAspectFit
let imageItem = UIBarButtonItem.init(customView: logoImageView)
let negativeSpacer = UIBarButtonItem.init(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
negativeSpacer.width = -25
self.navigationItem.leftBarButtonItems = [negativeSpacer,imageItem]
// Do any additional setup after loading the view.
}
}
Now every single viewController in your project can extend from BaseViewController
class ViewController: BaseViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
}
}
Make sure you call super.viewDidLoad in viewDidLoad
Hope it helps
Please change x position of logoImageView while setting its frame.
You are sets it's value as -40 might be because of that you are not able to see that logoImage.
Change it's x position to 0.

Navigation Title background image not working

This my simulator screen shot
import UIKit
class NewsTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
addCustomNavigationBarToView()
}
// This is my custom navigation bar Function
func addCustomNavigationBarToView(){
// Here i create a navigationBar variable
let navigationBar = UINavigationBar(frame: CGRect(x:0, y:0, width: self.view.frame.size.width, height: 100))
let navBackgroundImage:UIImage! = UIImage(named: "BarBa.jpg") self.navigationController?.navigationBar.setBackgroundImage(navBackgroundImage,for: .default)
//i was created navigation item variable and setup an image
let navigationItem = UINavigationItem()
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 220, height: 90 ))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "logo.png")
imageView.image = image
navigationItem.titleView = imageView
// Create left and right button for navigation item
let leftButton = UIBarButtonItem(title: "Save", style: UIBarButtonItemStyle.plain, target: self, action: Selector(("saveButtonClicked:")))
navigationItem.leftBarButtonItem = leftButton
//tapping the menu button work
// Assign the navigation item to the navigation bar
navigationBar.items = [navigationItem]
self.view.addSubview(navigationBar)
}
}
In addCustomNavigationBarToView function you have written wrong code:
Your TableViewController don't have any NavigationBar but you are currently setting background Image to self.navigationController.navigationBar :
{ self.navigationController?.navigationBar.setBackgroundImage(navBackgroundImage,for: .default)
}
You need to change your code to:
{
navigationBar.setBackgroundImage(navBackgroundImage,for: .default)
}

Swift : Logo on navigation bar along with back button

I want to have the logo of my app followed by the app name on the navigation bar. Along with this, there should be a back button.
Below is a screenshot :
I've tried the code below.
self.navigationItem.setHidesBackButton(false, animated:true);
let imgLogo : UIImage = UIImage(named:"Logo")!
let imgViewLogo : UIImageView = UIImageView(image: imgLogo)
imgViewLogo.frame = CGRectMake(0, 0, 40, 40)
let leftItem:UIBarButtonItem = UIBarButtonItem(customView: imgViewLogo)
self.navigationItem.leftBarButtonItem = leftItem
// App Name set on storyboard at design-time
This places the logo on top of the back button shown below.
How can this be achieved?
You can use custom view for navigationItem.titleView. Create UIView with your logo and a label for UIViewController title and set
navigationItem.titleView = YOUR_CUSTOM_VIEW;
Instead of using .leftBarButtonItem use:
let imgLogo : UIImage = UIImage(named:"Logo")!
let imgViewLogo : UIImageView = UIImageView(image: imgLogo)
imgViewLogo.frame = CGRectMake(0, 0, 40, 40)
let leftItem:UIBarButtonItem = UIBarButtonItem(customView: imgViewLogo)
self.navigationItem.leftBarButtonItems?.append(leftItem)
It will be added along with the system back button
You can find tons of example about it.
In this example I've added also a space to centered well your logo if you needed:
let leftButton: UIBarButtonItem = UIBarButtonItem(image: UIImage(named: "Logo")!, style: UIBarButtonItemStyle.Plain, target: self, action:#selector(MyViewController.leftButtonPress(_:)))
let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
negativeSpacer.width = -5.0 // Left inset to better center your logo
navigationItem.leftBarButtonItems = [negativeSpacer,leftButton]
func leftButtonPress(sender: AnyObject?) {
// do whatever you want when you press back button
}

Swift Custom NavBar Back Button Image and Text

I need to customise the look of a back button in a Swift project.
Here's what I have:
Here's what I want:
I've tried creating my own UIBarButtonItem but I can't figure out how to get the image to be beside the text, rather than as a background or a replacement for the text.
let backButton = UIBarButtonItem(title: "Custom", style: .Plain, target: self, action: nil )
//backButton.image = UIImage(named: "imageName") //Replaces title
backButton.setBackgroundImage(UIImage(named: "imageName"), forState: .Normal, barMetrics: .Default) // Stretches image
navigationItem.setLeftBarButtonItem(backButton, animated: false)
You can do something like that:
let yourBackImage = UIImage(named: "back_button_image")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationBar.backItem?.title = "Custom"
Your image will only have one color though
Note: Please remember that the back button belongs to the the source ViewController and not to the destination ViewController. Thus, the modification needs to be done in the source VC, which is reflected to all the view in the navigation controller
Code Snippet:
let backImage = UIImage(named: "icon-back")
self.navigationController?.navigationBar.backIndicatorImage = backImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
/*** If needed Assign Title Here ***/
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)
swift 4
In my case, I needed to have only the image of the button, without any text. I hope this will be useful to someone.
let imgBackArrow = UIImage(named: "back_arrow_32")
navigationController?.navigationBar.backIndicatorImage = imgBackArrow
navigationController?.navigationBar.backIndicatorTransitionMaskImage = imgBackArrow
navigationItem.leftItemsSupplementBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil)
For iOS 12 you can do
func setNavigationBar() {
self.navigationItem.setHidesBackButton(true, animated:false)
//your custom view for back image with custom size
let view = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
if let imgBackArrow = UIImage(named: "icn_back_arrow") {
imageView.image = imgBackArrow
}
view.addSubview(imageView)
let backTap = UITapGestureRecognizer(target: self, action: #selector(backToMain))
view?.addGestureRecognizer(backTap)
let leftBarButtonItem = UIBarButtonItem(customView: view ?? UIView())
self.navigationItem.leftBarButtonItem = leftBarButtonItem
}
#objc func backToMain() {
self.navigationController?.popViewController(animated: true)
}
For setting custom back bar button and remove text from back bar button,
FROM STORYBOARD only, without any coding.
RESULT:
For the back button image:
By this tutorial: (but didn't work for me)
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "imageName")
But this stack answer: (worked for me)
var backButtonImage = UIImage(named: "back-button-image")
backButtonImage = backButtonImage?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 30)
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)
And for the font, assuming you want the font to match for the whole navigation bar:(currently in use)
if let font = UIFont(name: "Avenir-Book", size: 22) {
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: font]
}
Having a button in Navigation Bar with Image AND Text is quite hard. Especially after they have introduced a new headache with UIBarButtonItem position in iOS 11: iOS 11 - UIBarButtonItem horizontal position
You can make either button with image or a button with text, but not a button with both of those. I even tried two UIBarButtonItems together, one with image and other with text - it still doesn't look good at all and their UIStackView can't be easily accessed for modification.
Unexpectedly I found a plain simple solution:
1) design the button as view in Interface Builder. In my case it is inside target UIViewController and accessible via IBOutlet for simplicity
2) set Leading Space constraint for the image to be negative, you might also want to set view's background color to .clear.
3) use it:
#IBOutlet var backButtonView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem(customView: self.backButtonView)
self.backButtonView.heightAnchor.constraint(equalToConstant: 44).isActive = true // if you set more than you'll get "Unable to simultaneously..."
self.backButtonView.widthAnchor.constraint(equalToConstant: 75).isActive = true
self.navigationItem.leftBarButtonItem = backButton
}
That's it. No need to use the trick with negative spacer for iOS 10 or the trick with imageInsets for iOS 11 (which works only if you have image and doesn't work for image+text, BTW).
I have tried all the above and all make the custom image without changing the text
The only one worked for me is from this answer
let backBTN = UIBarButtonItem(image: UIImage(named: "Back"),
style: .plain,
target: navigationController,
action: #selector(UINavigationController.popViewController(animated:)))
navigationItem.leftBarButtonItem = backBTN
navigationController?.interactivePopGestureRecognizer?.delegate = self
swift 3
extension UIViewController {
func setBackButton(){
let yourBackImage = UIImage(named: "backbutton")
navigationController?.navigationBar.backIndicatorImage = yourBackImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
}
}
This worked for me on iOS 13 using swift 5. Just hide the original back button and add a new navigation left bar button item with an action.
navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "backBtn"), style: .plain, target: self, action: #selector(back(sender:)))
#objc func back(sender: UIBarButtonItem) {
self.navigationController?.popViewController(animated:true)
}
I know it was answered. Here you can set title, image and target.
let view = UIView()
let button = UIButton(type: .system)
button.setImage(UIImage(named: "backArrow_theme"), for: .normal)
button.setTitle("Back to workflow", for: .normal)
button.addTarget(self, action: #selector(onBackButton(_:)), for: .touchUpInside)
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: -10)
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view)
Just replace the backButton with a custom rightBarButtonItem
let backImage = UIImage(named: "BackBtn")?.withRenderingMode(.alwaysOriginal)
navigationItem.leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(popnav))
#objc func popnav() {
self.navigationController?.popViewController(animated: true)
}
Just in case someone need to change all Back buttons color or font with Swift5. UIBarButtonItem.appearance().tintColor = .red
Add this to AppDelegate.swift file.
import UIKit
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIBarButtonItem.appearance().tintColor = .white
UIBarButtonItem.appearance().setTitleTextAttributes([
NSAttributedString.Key.foregroundColor: .red,
NSAttributedString.Key.font: UIFont(name: "font_name", size: 14)!
], for: .normal)
return true
}
}
iOS13 And Later, Try to use UINavigationBarAppearance
let appearance = UINavigationBarAppearance()
// set back image
appearance.setBackIndicatorImage(UIImage(named: "back_icon"), transitionMaskImage: UIImage(named: "back_icon"))
// set appearance to one NavigationController
let navVC = UINavigationController()
navVC.navigationBar.standardAppearance = appearance
navVC.navigationBar.scrollEdgeAppearance = appearance
// or you can config for all navigation bar
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
back title base on you Viewcontroller
viewController.navigationItem.backButtonTitle = "your back title"
Swift 4.2
Add this functions ViewController
func addNavigationBarButton(imageName:String,direction:direction){
var image = UIImage(named: imageName)
image = image?.withRenderingMode(.alwaysOriginal)
switch direction {
case .left:
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
case .right:
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
}
}
#objc func goBack() {
navigationController?.popViewController(animated: true)
}
enum direction {
case right
case left
}
Using you should use here
viewDidLoad()
addNavigationBarButton(imageName: "ic_back", direction:.left)
Changing the navigation controller`s standardAppearance or scrollEdgeAppearance will reset your custom backIndicatorImage and backIndicatorTransitionMaskImage
let backImage = UIImage(systemName: "chevron.left.circle.fill")
navigationController?.navigationBar.backIndicatorImage = backImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
// this will cause the backIndicatorImage to be reset
let standardAppearance = UINavigationBarAppearance()
navigationController?.standardAppearance = standardAppearance
To change the backIndicatorImage in conjunction with UINavigationBarAppearance you will need to set the backImage using this:
navigationController?.standardAppearance.setBackIndicatorImage(backImage, transitionMaskImage: backImage)
You can change it globally in the AppDelegate with the following code:
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "custom-back")
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "custom-back")

Space issue with UIBarButtonItem

I am trying to add a Cancel UIBarButtonItem to my navigation bar using following code:
func setupNavBar() {
self.navBar = UINavigationBar(frame: CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.width, 64.0))
let customNavigationItem = UINavigationItem(title: "Connect Accounts")
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "cancelClicked")
customNavigationItem.setLeftBarButtonItem(cancelButton, animated: true)
self.navBar.setItems([customNavigationItem], animated: true)
self.view.addSubview(self.navBar)
}
The bar button is appearing completely sticked to the screen edge as follow:
Why is this button appearing sticked to the edge of screen and how can I give it spacing so that it does not sticks there? Please help!
EDIT: My button is sticked to just left edge of screen and not to the top-left corner.
The correct way to add space is using .FixedSpace :
let fixedSpace = UIBarButtonItem(barButtonSystemItem: .FixedSpace, target: nil, action: nil)
fixedSpace.width = 10
customNavigationItem.leftBarButtonItems = [fixedSpace, cancelButton]
UIBarButtonSystemItemFixedSpace
Blank space to add between other
items. Only the width property is used when this value is set.
Available in iOS 2.0 and later.
Try this
let btn = UIButton(frame: CGRectMake(<your margin>, 0, 50, 44))
btn.setTitle("Cancel", forState: UIControlStateNormal)
let btnbarButton = UIBarButtonItem(customView: btn)
customNavigationItem.setLeftBarButtonItem(btnbarButton, animated: true)
Try it.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setupNavBar()
}
func setupNavBar() {
let navBar = UINavigationBar(frame: CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.width, 64.0))
let customNavigationItem = UINavigationItem(title: "Connect Accounts")
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: "cancelClicked:")
let fixedSpace = UIBarButtonItem(barButtonSystemItem: .FixedSpace, target: nil, action: nil)
fixedSpace.width = 10
customNavigationItem.leftBarButtonItems = [fixedSpace, cancelButton]
navBar.setItems([customNavigationItem], animated: true)
self.view.addSubview(navBar)
}
func cancelClicked(sender: AnyObject){
print("Good luck!")
}

Resources