iOS custom back button bad transition - ios

I want to customize the image for a back button in just a viewController.
So for this viewController i have:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.backIndicatorImage = #customImage
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.backIndicatorImage = #restoreImage
}
But when im displaying the previous viewController (viewWillDissapear is called) this previous viewController wait until its displayed to set the image (if i swippe this doesnt happen):

Here, changes of image (imageSize 40 * 40) working fine. You can try this.
SecondViewController:
override func viewWillAppear(_ animated: Bool) {
var backButtonImage = UIImage(named: "lineBack.png")
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)
}
ThirdViewController:
override func viewWillAppear(_ animated: Bool) {
var backButtonImage = UIImage(named: "roundBack.png")
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)
}

Related

iOS: Hide the navigation bar for only one viewcontroller which is root of the UINavigationController?

I want to hide the navigationbar for only one viewcontroller which is the root viewcontroller of the UINavigationController.
Currently I am using below code to hide the navigation bar for a particular viewcontroller.
To hide the navigationbar,
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
super.viewWillAppear(animated)
}
To show the navigationbar for other viewcontrollers,
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
super.viewWillDisappear(animated)
}
When I am trying to use this code, the app is being crashed in iOS 13 devices because of threading violation: expected the main thread.
Please checkout the issue which I am getting when I use the above code to hide the navigationbar,
iOS 13: threading violation: expected the main thread
Please let me know if there is any other way to hide the navigationbar for only one viewcontroller.
I got the another way to hide/show navigationbar from one of my friend.
Set a delegate for the NavigationController:
navigationController.delegate = self
Hide/Show navigationbar for each ViewController all in one place
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let hide = (viewController is YourVC)
navigationController.setNavigationBarHidden(hide, animated: animated)
}
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool){
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
}
override func viewWillDisappear(_ animated: Bool){
super.viewWillDisappear(animated)
self.navigationController?.isNavigationBarHidden = false
}
}
You can make it transparent (Completely invisible) when viewWillApper get called and back to normal when view willDisappear get called. Here are helper functions.
func makeNaBarTransparent() {
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.isTranslucent = true
}
func restoreNavigationBarToDefault() {
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.shadowImage = nil
}
USAGE
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
makeNaBarTransparent()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
restoreNavigationBarToDefault()
}
}

How to hide the navigation bar shadow only in detail view?

There are many answers in SO that provide solutions for hiding the navigation bar shadow. Those work for me except for this particular case, which I'm describing here. Therefore, this question is not a duplicate.
To test this particular case, I created a new project using the master-detail app template. In the DetailViewController -> viewDidAppear, I coded the following:
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
The above code works for a Single View App and on iPad Air 2 simulator. However, it doesn't work on the detailViewController of a master-detail app in iPhoneX simulator.
Alternatively, I also tried retrieving the subviews of the navigationBar in viewDidAppear and tried hiding the shadow (see code below). However, the subview count is zero. How could that be?
for parent in self.navigationController!.navigationBar.subviews {
for childView in parent.subviews {
if(childView is UIImageView) {
childView.removeFromSuperview()
}
}
}
Any help on this is much appreciated.
For Generic flow
You could use this setup. Say ViewController is the all other view controller class (where you want the shadow), and DetailViewController is the detail view controller class.
The thing i'm doing preserving the shadow image.
ViewController.swift
class ViewController: UIViewController {
var shadowImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
shadowImage = self.navigationController?.navigationBar.shadowImage
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = shadowImage
}
}
And DetailViewController.swift
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.navigationController?.navigationBar.shadowImage = UIImage()
}
}
Storyboard setup
Output
Note: Another neat approach would be storing the shadow within the DetailsViewController and setting it while the view is about to disappear
class DetailsViewController: UIViewController {
var shadowImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
shadowImage = self.navigationController?.navigationBar.shadowImage
self.navigationController?.navigationBar.shadowImage = UIImage()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = shadowImage
}
}
This solution is more elegant and resulting in a clean management.
For MasterDetailFlow, Using SplitViewController
In your MasterViewControlelr.swift
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = splitViewController!.isCollapsed // placeholder code when you created the project
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = nil
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
In your DetailViewController.swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.navigationBar.shadowImage = nil
}
Output(Master/Detail flow)

iOS - Navigation Bar transparent transition

I want to make a Navigation Bar that goes transparent in detail but with my current code the Bar doesn't return to it's non transparent state. How can this be fixed? I want to this code to also work in the MoreNavigationController from the UITabBarController.
The code that has been placed in the Detail ViewController.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self?.navigationController?.navigationBar.shadowImage = UIImage()
}, completion: { context in
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.transitionCoordinator?.animate(alongsideTransition: { [weak self](context) in
self?.navigationController?.navigationBar.setBackgroundImage(nil, for: UIBarMetrics.default)
self?.navigationController?.navigationBar.shadowImage = nil
}, completion: { context in
})
}
Add the code below to DetailViewController.
It was confirmed that this code also works in the UINavigationController from the UITabBarController.
NextController.swift
class NextViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setNavigationBar()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
self.navigationController?.navigationBar.shadowImage = nil
self.navigationController?.navigationBar.isTranslucent = true
}
func setNavigationBar() {
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = false
}
}
Preview

Disable animation with backBarButtonItem

As my title saying is there any way to customize default back button action method ?
I tried to find on Google but not get any answer that fulfill my requirement.
I know one way to Add UIBarButtonItem to self.navigationItem.leftBarButtonItem and customize action method but I will be hide < sign of Back button and I don't want this.
Some R&D that done by Google.
1) How to override self.navigationItem.backBarButtonItem Action?
2) iOS disable animation for NavigationController back button
3) Google Question/Answer
Add image that look like <Back button that is one way but I want to going with native way.
One option is to implement the viewWillDisappear method on the View Controller and set animationEnable No
override func viewWillDisappear(animated : Bool) {
super.viewWillDisappear(animated)
UIView.setAnimationsEnabled(false)
}
override func viewDidDisappear(_ animated: Bool) {
UIView.setAnimationsEnabled(false)
}
you have to add your custom bar button there...
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: YourDesiredFrame)
button.setImage(UIImage(named : "back"), for: .normal)
button.addTarget(self, action: #selector(backButtonTapped(_:)), for: .touchUpInside)
let barButton = UIBarButtonItem(customView: button)
self.navigationItem.backBarButtonItem = barButton
}
func backButtonTapped(_ sender : UIButton) {
_ = self.navigationController?.popViewController(animated : false)
}
// Swift 4
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// turn on animations
UIView.setAnimationsEnabled(true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// turn off animations
UIView.setAnimationsEnabled(false)
}

Inside tabbar viewcontrollers navigationbar changes not working from storyboard

I have created
NavigationController(Main) - > LoginViewController -> Tabbarviewcontroller -> HomeViewController
If I add barbutton item in HomeViewController through storyboard it's not displaying in simulator.
But I can see the changes in storyboard.
Title Home1 and barbutton item not displaying in simulator
Try this Code: Tested in Swift 3:
Note: Delete all your barButtonItems and try below code.
Add this code to your Home1 VC:
override func viewWillAppear(_ animated: Bool) {
let RightButtomitem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(handler))
self.tabBarController?.navigationItem.rightBarButtonItem = RightButtomitem
}
func handler(sender:UIButton) {
print("Add Button pressed")
}
Add this code to your Home2 VC:
override func viewWillAppear(_ animated: Bool) {
let RightButtomitem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handler))
self.tabBarController?.navigationItem.rightBarButtonItem = RightButtomitem
}
func handler(sender:UIButton) {
print("Done Button Pressed")
}
Output:
First you hide Navigation Bar in NavagationController and you make custome bar After you Add in it which you want in bar .
You can try it programmatically
HomeViewController ---
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
let barButtomitem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: #selector(handler))
self.tabBarController?.navigationItem.rightBarButtonItem = barButtomitem
}
SecondItemViewController--
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.tabBarController?.navigationItem.rightBarButtonItem = nil
}
and don't add barbutton in storyboard ...

Resources