swift UIViewController reload inputAccessoryView - ios

I have a structure:
UITabBarController (2 tabs) -> UISplitViewController -> UINavigationController -> ViewController
In first viewcontroller I replaced tabbar with inputAccessoryView
class AViewController: UIViewController {
override var inputAccessoryView: UIView? {
return someView()
}
}
in AViewController I programmatically need show the second tabbar item
func presentSecondTabBar() {
let tabbar = self.tabBarController
tabbar?.selectedIndex = 1
}
and when I present the second tabbar item then inputAccessoryView will replace with tabbar BUT when come back to AViewController (pressing to first tabBar item in tabBarController) AViewController still have tabbar, doesn't present a inputAccessoryView
I tried to reload the inputAccessoryView in AViewController, but didn't help.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
inputAccessoryView?.becomeFirstResponder()
view.reloadInputViews()
view.layoutIfNeeded()
inputAccessoryView?.updateConstraints()
inputAccessoryView?.setNeedsLayout()
inputAccessoryView?.layoutIfNeeded()
inputAccessoryView?.reloadInputViews()
}
Can u help me?

Related

Resetting the navigation stack on a UITabBarController doesn't work

I'm trying to reset the navigation stack on a subclass of UITabViewController embedded in a UINavigationController but it doesn't work.
My navigation stack, which I create programmatically, is like this:
UINavigationController => ControllerA (a subclass of UIViewController) =>
ControllerB (a subclass of UIViewController) => ControllerC (a
subclass of UITabBarController).
When users press on the "Back" button or swipe back from ControllerC, the app should go back to ControllerA, not ControllerB.
Usually, when I want to reset the navigation stack, I do this in the Controller's viewDidLoad() method:
override func viewDidLoad() {
super.viewDidLoad()
// usually work, but not in a subclass of UITabBarController as self.navigationController is nil
if let navigationController = self.navigationController {
// keep only the root controller (0) and the current controller
navigationController.viewControllers = [navigationController.viewControllers[0], self]
}
}
but this doesn't work in ControllerC (the subclass of UITabViewController) as self.navigationController is nil.
If I do this instead (still in ControllerC's viewDidLoad() method):
/// ControllerC's viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
if let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
// keep only the root controller (0) and the current controller
navigationController.viewControllers = [navigationController.viewControllers[0], self]
}
}
This works, but then there is no animation between ControllerB and ControllerC when I do:
controllerB.navigationController?.pushViewController(ControllerC(), animated: true)
I also tried to override ControllerC's viewWillDisappear() method:
/// ControllerC's viewWillDisappear
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
if let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
navigationController.popToRootViewController(animated: true)
}
}
This works, but ControllerB is briefly visible before ControllerA is shown.
Any help would be greatly appreciated!
In the ControllerC instead of trying to override viewWillDisappear() method you can override viewDidAppear() like that:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navC = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
// keep only the root controller (0) and the current controller
navC.viewControllers = [navC.viewControllers[0], self]
}
}
And ControllerB won’t be briefly visible before ControllerA when you navigate backwards.

How to set viewcontroller of UITabbarItem to default viewcontroller when switching tabs?

I have a UITabBarController. It has 4 tabBarItem.
Suppose I'm at 1st tabBarItem defaultViewController(1) and I
went to another ViewController(2) which is shown after some
actions in first defaultViewController(1).
Then I switched to 2nd tabBarItem defaultViewcontroller(2).
Again I switched back to 1st tabBarItem, it shows ViewController(2).
I want to show defaultViewController(1). How can I achieve this using swift 4.
defaultViewController(1) and defaultViewController(2) are the default ViewController for 1st and 2nd TabBarItem respectively.***
Let's say you have UINavigationController inside each tab of UITabBarController, and defaultViewController(1) is the rootViewController of your first tab, inside that there is a button that navigate to ViewController(2).
For this first of all, let's create generic solution. Create UIApplication Extension like this,
extension UIApplication {
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let navigationController = controller as? UINavigationController {
return topViewController(controller: navigationController.visibleViewController)
}
return controller
}
}
Implement UITabBarControllerDelegate in AppDelegate and do below code,
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if tabBarController.selectedIndex == 0 {
UIApplication.topViewController()?.navigationController?.popToRootViewController(animated: false)
}
}
In above code, I have taken tabBarController.selectedIndex to 0, you can make it different depending on your requirement.
Let me know in case of any queries.
Extend a subclass of UITabbarController and use it as your tabbar's class. In that implement UITabBarControllerDelegate, didSelect and use popToRootViewController to pop to your defaultViewController.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController){
if viewController is UINavigationController {
//when you have `UINavigationController`
let rootNavigationController = viewController as! UINavigationController
rootNavigationController.popToRootViewController(animated: false)
} else {
//when you don't have `UINavigationController` then dismiss all viewcontroller that was presented.
let rootViewController = viewController
if rootViewController.presentingViewController != nil {
rootViewController.dismiss(animated: false, completion: nil)
}
}
}
Note: add self.delegate = self to conform the protocol inside viewDidLoad method.

How can I hide the tab bar for a single view controller in a navigation controller stack

I have a UITabBarController. One of the tabs contains a UINavigationController.
I'd like to push a view controller onto the navigation stack and hide the tab bar on that view controller. I can do this easily with:
toVC.tabBarController?.hidesBottomBarWhenPushed = true
self.navigationController?.pushViewController(toVC, animated: true)
or doing it in the storyboard:
The problem is, this hides the tab bar for any subsequent view controllers I push onto the stack. I'd like to simply hide the tab bar for this one view controller and show it for all other view controllers before and after it.
There is a workaround. It works the way it is presented on gif below.
For each UIViewController that is pushed into the UINavigationController stack I override the hidesBottomBarWhenPushed property this way:
override var hidesBottomBarWhenPushed: Bool {
get {
switch navigationController?.topViewController {
case .some(let controller):
switch controller == self {
case true:
return super.hidesBottomBarWhenPushed
case false:
return false
}
default:
return super.hidesBottomBarWhenPushed
}
}
set {
super.hidesBottomBarWhenPushed = newValue
}
}
The first switch checks whether this controller belongs to some UINavigationController stack. The second switch checks whether current top UIViewController of UINavigationController stack is self.
Hope it will work in your case. Happy coding (^
If you hide on the storyboard then by this property your tab bar will hide for all the view controllers. So you can manage this by code.
You can do this programmatically by just writing one line of code in ViewDidLoad() or ViewWillAppear() method
For Swift 3:-
self.tabBarController?.tabBar.isHidden = true
And where you want to unhide the tab bar just write the following code in ViewDidLoad () or ViewWillAppear() method
self.tabBarController?.tabBar.isHidden = false
Try this in the view controller you want to hide the tab bar in:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = true
}
And this in the view controllers before and after the one you want to hide the tab bar in:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
}
EDIT:
Fully implemented example:
class ViewController1: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
}
}
class ViewController2: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = true
}
}
class ViewController3: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = false
}
}

Bottom tabBar controller not hiding when I pass to another viewController

I am making a project where the first MainPage is TabBarController(MainTabController). But then I am passing to another Viewcontroller there is one more tabbetViewControllers(secondTabbedController). And now When I pass to secondTabbedController the tabs of MainTabController are not hiding. There Should be secondTabBarController Items but there tab items of first(MainTabBarController). I guess that it is because of the navigationController and If I delete it it is fixes. But I need this NavigationController. How to fix it ?
This is ArticlesViewController that You can find in first image:
Here is the solution:
write this code in first viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this will show the tabbar when come back to first viewcontroller
self.tabBarController?.tabBar.isHidden = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// this will hide the tabbar when moved next viewcontroller
self.tabBarController?.tabBar.isHidden = true
}
Just true this "hidesBottomBarWhenPushed" when you are pushing view controller
let objViewController: ProductDetailsViewController? = UIStoryboard.mainStoryboard().instantiateViewController(withIdentifier: "ProductDetailsViewController") as? ProductDetailsViewController
objViewController?.hidesBottomBarWhenPushed = true
navigationController?.pushViewController(objViewController ?? UIViewController(), animated: true)
And you can also set this in ViewController XIB "Attribute Inspector" by just clicking "Hide Bottom Bar on Push".

Determine viewWillAppear from Popped UINavigationController or UITabBarController

I am unable to find a way to distinguish between popping from the Nav controller stack and entering the view controller from the UITabBarController.
I want to call a method in ViewWillAppear only when the view is presented from the TabBar, not when someone presses back in the navigation controller.
If I wasn't using a TabBarController, I could easily get this functionally using viewDidLoad.
I've tried,
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
println("View Will Appear")
if isBeingPresented() {
println("BP")
}
if isMovingFromParentViewController() {
println("from")
}
if isMovingToParentViewController() {
println("to")
}
}
But there is no difference when I present from pressing the Tab Button or when press back button.
Only the "View Will Appear" is getting called.
Using iOS 8.4 / Swift
Sounds like a good use of the UITabBarControllerDelegate.
First, add a Bool property on your ViewController comingFromTab:
class MyViewController: UIViewController {
var comingFromTab = false
// ...
}
Set your UITabBarControllerDelegate to whatever class you want and implement the method shouldSelectViewController. You may also want to subclass UITabBarController and put them in there.
func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
if let myViewController = viewController as? MyViewController {
myViewController.comingFromTab = true
}
If your tab's initial view controller is a UINavigationController, you will have to unwrap that and access it's first view controller:
if let navController = viewController as? UINavigationController {
if let myViewController = navController.viewControllers[0] as? MyViewController {
// do stuff
}
}
Lastly, add whatever functionality you need in viewWillAppear in your view controller:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// ...
if comingFromTab {
// Do whatever you need to do here if coming from the tab selection
comingFromTab = false
}
}
There is no way to know for sure. So I guess the easiest way is to add some variable that you will have to change before popping back to that view controller and checking it's state in viewWillAppear.
class YourViewController: UIViewController {
var poppingBack = false
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if !poppingBack {
// your logic
}
else {
poppingBack = false // reset it for next time
}
}
}
// somewhere else in code, suppose yourVC is YourViewController
yourVC.poppingBack = true
self.navigationController.popToViewController(yourVC, animated: true)
You can also try implementing UINavigationControllerDelegate's - navigationController:willShowViewController:animated: method and check if it will be called when presenting your view controller from tab bar.
You can check parentViewController property
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if parentViewController is UITabBarController {
// Presented by UITabBarController
} else if parentViewController is UINavigationController {
// Presented by UINavigationController
} else {
// Presented by ...
}
}

Resources