Search bar not aligned next to back button in navigation bar - swift

I used a custom navigation controller class to remove the back button in my navigation bar, with the following:
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
}
}
In one of my VCs, I also add a search controller to the navigation bar with the following:
private func configureSearchController() {
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = true
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
}
However, it seems like the search bar is still being offset by some blank space where the title would be. I'd image the search bar should be aligned next to the back button, but there is the back button, space with blank title, then search bar.
How can I get the search bar aligned next to the back button instead of spaced because it seems there is some room for the blank title. Also, as a note, tapping on that blank space moves me back to the previous VC, although there is no actual title there.
EDIT:
View hierarchy, looks like the back button container view itself takes up a good amount of space

Try adding this in viewDidLoad
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
backButton.setBackButtonTitlePositionAdjustment(UIOffsetMake(-1000, -1000), for: .default)
navigationItem.backBarButtonItem = backButton

Related

Open swiftUI view from UIKit code for rightBarItem

This is code of UIKit File.I want to open view which is editButtonCustomizeView and written in swiftUI. How can I take action of this button which navigates to swiftUI
override func viewDidLoad() { super.viewDidLoad()
let editButton = UIBarButtonItem(image: editImage, style: .Plain,
target: self, action: editButtonCustomizeView())
}
Right now getting error is "Cannot convert value of type 'editButtonCustomizeView' to expected argument type 'Selector?' "
That is because the action expects a selector to point to what action is to be called when the button is tapped so you can't pass a view.
If you want to show a SwiftUI view when that button is tapped you have to first add a action to show the viewcontroller, then create the action to show the viewcontroller and lastly add you SwiftUI view in a viewcontroller so it can be used in UIKit.
Adding the action to the button:
let editButton = UIBarButtonItem(image: editImage, style: .Plain, target: self, action: #selector(showSwiftUIView))
Then the action to show the view:
#objc func showSwiftUIView(sender: UIControl) {
let vc = editButtonCustomizeVC()
present(vc, animated: true)
}
And lastly turn your view to a ViewController:
final class editButtonCustomizeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIHostingController(rootView: editButtonCustomizeView())
addChild(childView)
childView.view.frame = view.bounds
view.addSubview(childView.view)
childView.didMove(toParent: self)
}
}

UIButton is only responsive sometimes when pressing down [duplicate]

I have a UIBarButtonItem in the right side of my navigation that has an image of a gear and presents my settings view controller. I can get it to work properly when I create the button in setupNavigationBar(), but it doesn't work if I create the button as a property. I can't wrap my head around what would be different about these two scenarios. The button is present in both situations, but the functionality isn't.
This version doesn't work
class DecksController: UIViewController {
let settingsBarButton: UIBarButtonItem = {
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
return barButton
}()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
This version does work
class DecksController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
let settingsBarButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
As you've discovered, it makes a big difference where this line occurs:
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"),
style: .plain, target: self, action: #selector(presentSettings))
The problem is the target:self part. When the bar button item is configured as part of an instance property initializer (your first example), the instance doesn't exist yet — it is what we are initializing. So self has no meaning, and the button ends up with no target. Therefore, tapping the button does nothing.
(Actually, to be quite technical, self is the class, but that's not a helpful thing to know.)
In your second example, that line is part of viewDidLoad, which runs considerably after the view controller instance has come into existence and has been initialized. viewDidLoad is an instance method, in fact. So self is the instance, as you expect.

How to set title of navigation item after open another view controller using with present modally - Swift

I have two view controllers in my application. Root view controller has some components and one of them is a button. That button provides to open another view controller using with present function. Second view controller(SelectTimeViewController) that opens when the button is tapped, was opened successfully. I am trying to set navigation title and items but I can not see them.
I did not use storyboard so root view controller is setting from AppDelegate.
let viewController = ViewController()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: viewController)
window?.makeKeyAndVisible()
When tapped the button, "openVC" function is invoked.
#IBAction func openVC(_ sender: Any) {
self.navigationController?.present(SelectTimeViewController(), animated: true, completion: nil)
}
I am trying to set title and rightBarButtonItem in SelectTimeViewController's viewDidLoad function but I can not see both of them.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "close"), style: .plain, target: self, action: #selector(closeIt))
self.navigationItem.title = "Select Time"
}
In additional to this, I can see both title and right bar button item when change the "openVC" function like as bellow.
#IBAction func openVC(_ sender: Any) {
self.navigationController?.pushViewController(vc, animated: true)
}
You have to push the SelectTimeViewController instead of present
Or if you really want to present it, you should present another UINavigationController that has the rootViewController as SelectTimeViewController()

UIBarButtonItem doesn't work when created as a property, but does when created in a function

I have a UIBarButtonItem in the right side of my navigation that has an image of a gear and presents my settings view controller. I can get it to work properly when I create the button in setupNavigationBar(), but it doesn't work if I create the button as a property. I can't wrap my head around what would be different about these two scenarios. The button is present in both situations, but the functionality isn't.
This version doesn't work
class DecksController: UIViewController {
let settingsBarButton: UIBarButtonItem = {
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
return barButton
}()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
This version does work
class DecksController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
let settingsBarButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
As you've discovered, it makes a big difference where this line occurs:
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"),
style: .plain, target: self, action: #selector(presentSettings))
The problem is the target:self part. When the bar button item is configured as part of an instance property initializer (your first example), the instance doesn't exist yet — it is what we are initializing. So self has no meaning, and the button ends up with no target. Therefore, tapping the button does nothing.
(Actually, to be quite technical, self is the class, but that's not a helpful thing to know.)
In your second example, that line is part of viewDidLoad, which runs considerably after the view controller instance has come into existence and has been initialized. viewDidLoad is an instance method, in fact. So self is the instance, as you expect.

How To Remove Navigation Controller Back Button Text [duplicate]

This question already has answers here:
Remove text from Back button keeping the icon
(32 answers)
Closed 4 years ago.
When you create a navigation controller in the storyboard and create a segue to another view controller you automatically get this back button with an arrow and the title of previous page. (see image)
Image of problem
How can I delete the text and still have the arrow there?
or
How can I replace with my own image?
Thanks!
Inside your viewDidLoad of the setting you can use this to change the title
let backButton = UIBarButtonItem()
backButton.title = "" //in your case it will be empty or you can put the title of your choice
self.navigationController?.navigationBar.topItem?.backBarButtonItem = backButton
and this to set your own image
let backImage = UIImage(named: "backButton")
self.navigationController?.navigationBar.backIndicatorImage = backImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
You can implement UINavigationControllerDelegate like this:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let item = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
viewController.navigationItem.backBarButtonItem = item
}
it affects next pushed controller backBarButtonItemtitle.
edit:
in your ViewController
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.delegate = self
}