A little background on the setting of our views:
Inside a NavigationController, we have a UITabBarController (with 3 tabs) with a UIViewController that has a UISearchController.
There is an error that if we leave the UISearchController active and switch to another view, when we return to the search view the entire screen is black.
However, when the UISearchController is not active and we switch views this does not happen.
We have tried to set the controller to not be active when segueing between views; however, when the UISearchController is active none of the segueing events get called (no log prints appear from viewWillDissapear, viewWillAppear, etc.)
Looking on other threads, we tried setting self.definesPresentationContext = true
but that does not work.
Has anyone else had this problem or know how to fix it?
Try to set the searchbarController active like this
self.resultSeachController.active = false
before you move on the next View
I faced the same problem and solved it as follows:
I extended UITabBarController and created a custom class TabBarController
class TabBarController: UITabBarController {
In that class I implemented its didSelectItem method, and in that method I called a method of the view controller that closes the search controller
// UITabBarDelegate
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
let vc = viewControllers![selectedIndex] as! CommonViewController
if vc.searchController.active {
vc.searchBarCancelButtonClicked_NoReload()
}
}
viewControllers is an array in UITabBarController that keeps all the view controllers belonging to the UITabBarController, and 'selectedIndex' is the index of the Tab (and the view controller) which was displayed, and thus one can get to the viewController that has the searchController active.
In my app all the view controllers are subclasses of a root class named CommonViewController where I put all the vars and methods that are common to all view controllers, such as all the search functionality. Therefore I simply check if the search controller is active and if it is I call a method that makes it inactive and does some other related cleanup.
Related
I have an app with a HomeView and a bottom TabBar which load other views on tap. On this TabBar bar I also have a More button which on click opens a listView, each cell of this view also opens other views. (When I'm talking about views, I mean new tunnels/navigation controllers are displayed for example: MyPicturesViewController, SettingsViewController etc...).
There is my problem, I have to implement a feature that from my HomeView on a button click it navigates to a specific view that is not on the TabBar but in the More list and also update the TabBar to highlight the More button.
I tried at first to navigate manually with this code:
let viewController = UIStoryboard.instantiate(NewViewController.self)
self.viewController?.navigationController?.pushViewController(viewController, animated: true)
it works pretty well
and then update the tabbar manually
self.viewController?.tabBarController?.selectedIndex = 4
But updating the TabBar redirect automatically to the selected index which is the More list view and don't even take in count the manual navigation I did in the previous code.
So my question is, is it possible to update TabBar bar without loading its own navigationcontroller? If not how can I achieve what I want to do, I tried many things such as create a navigationcontroller and its viewcontrollers and replace the tabbar viewcontrollers stacks but I didn't succeed.
Thank you in advance for those who will take time to help me!
Instead doing it before
self.viewController?.tabBarController?.selectedIndex = 4
Push your controller in tabBar delegate method
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController == desiredNavigationController {
show(desiredNavigationController, animated: true) // push
return false
}
return true
}
As mentioned I would like to know if it is possible to have an underlying navigationcontroller handle gestures when showing an embedded controller, without delegate methods.
I'm trying to make a slide out menu bar, and currently it's the embedded viewcontroller that handles the sliding out of the menu by a delegate method. I would love to make it so that the embedded viewcontroller has no idea that it's embedded in a slide out menu controller.
For the hierachy I have ContainerViewController, this is responsible for the whole thing. ContainerViewController has a MenuViewController (this handles the slide out menu) and a NavigationController with HomeController as rootViewController. HomeController is the viewcontroller I embed in the navigationcontroller.
I would like for the ContainerViewController to handle the UIEdgePanGesture even when the embedded HomeController is being shown. Currently HomeController has a delegate method that alerts the ContainerViewController that a UIEdgePanGesture has been made and thus opens the menu.
I tried applying the UIPanGestureRecognizer to the NavigationControllers view instead, but to no avail.
This is what I'd like to avoid, as there's really no reason to have every viewcontroller have responsibilities to the slideMenu, as they shouldn't know the slide menu exists in the firstplace.
protocol HomeControllerDelegate {
func handleMenuToggle()
func handleEdgePan(sender: UIScreenEdgePanGestureRecognizer)
}
class HomeController: UIViewController {
#objc func handleMenuToggle() {
//print("Menu was toggled...")
delegate?.handleMenuToggle()
}
#objc func handleEdgePan(sender: UIScreenEdgePanGestureRecognizer) {
//print("Menu was edgePanned...")
delegate?.handleEdgePan(sender: sender)
}
}
My app is using Tab Bar Controller which contains several View Controller in different tab. When user open the app, they will firstly enter FirstView. I would like to put some method in SecondView which refresh the FirstView. This is my FirstViewController.swift:
class FirstViewController: UIViewController, UIScrollViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
I have tried to put
FirstViewController().viewDidLoad()
in my SecondViewController.swift, but this is not working. Is there any better way to refresh the FirstView?
Try this way by make a static reference of firstViewController and then through this reference you can call any function
class ViewController: UIViewController {
static var firstVC : ViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("m on FirstViewController ")
ViewController.firstVC = self
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("m on SecondViewController ")
ViewController.firstVC?.viewDidLoad()
}
}
you can try this:-
instead of 0 pass the index of your FirstViewController
if let firstVC = self.tabBarController?.viewControllers?[0] as? FirstViewController {
firstVC.viewDidLoad()
}
You should not call viewDidLoad yourself. Its called only once when the view is loaded.
If you want to update the controllers view just before its displayed, you can use viewWillAppear for changing the layout or whatever you want to do.
The issue you're battling it how to tell another view controller that it needs to update its view. For this, you have two potential solutions because effectively, you're determining the best way to communicate between different objects.
Notifications are loosely decoupled and tend to be useful for one to many relationships. One object can fire off a notification and one or more objects can be listening for that notification. In your situation, a notification can be broadcast when a certain piece of state has changed in one view controller, and the other view controller can observe that notification so it can be notified when it should change.
Delegates are more closely coupled because they're one to one. They are often times implemented by creating a delegate property on an object that conforms to some protocol. Another object then assigns that delegate property to itself and implements the protocol so its implementation will be invoked whenever that function is called on the delegate. In your situation, each view controller could have a delegate property for some protocol(s). The tab bar controller can assign the delegate property to itself and handle the implementations of these functions. Therefore, whenever a change happens and a delegate is invoked, the tab bar controller can take can responsibility of telling which view controllers to update their view.
There are also of course other ways of handling your situation such as updating the view in viewWillAppear. This way, whenever a view controller appears on the screen, some code can execute that will update its view.
It ultimately depends on how you're storing application state and the design of your application.
In my iOS app, I have a UITabBarController, and its viewControllers list looks like [vc1, vc2], which are of class MyVC1 and MyVC2 respectively, both of which subclass UIViewController. MyVC1 overrides viewWillAppear, and the overwritten version prints something so I know when it is called. (This is to isolate a larger problem I had, which was a view not appearing.)
My issue is that when the app first starts up and vc1 is the selected tab, nothing is printed, meaning its viewWillAppear is not being called. If I switch tabs and then go back to vc1, something does get printed, so vc1's viewWillAppear is not being called until I switch back to it from another tab.
Is there any way to have vc1 call viewWillAppear as soon as the app starts up, without needing to switch tabs? Honestly, I'm surprised this wasn't the default behavior already.
I just made a new test app in Xcode with nothing but a UITabViewController (and its two child view controllers) in Main.storyboard, and when I override viewWillAppear for the first child, my "view will appear" log prints every time (including on app launch).
Here are a couple things that could cause viewWillAppear to not be called:
Not calling super somewhere you've overridden viewWillAppear
Adding a view controller's view somewhere as a subview without adding the view controller as a child view controller
You also might try seeing if viewWillAppear is getting called for your UITabBarController, and if not, is it being called for its parent or presenting view controller? And so on until you find where the holdup is.
if you have Custom UITabBarController, check override func viewWillAppear is call super
in Custom UITabBarController
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // this line need
}
Use the following line in viewWillAppear method of your vc1:
[super viewWillAppear:animated];
Next, the UITabBarController must be your initial controller.
make shure in the UITabBarController in the viewwillapear have the super.viewWillAppear(animated) like the following code
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
So, I have an app which has as its root view controller a menu bar controller class which I named MenuTabBarController, and which holds a number of UIViewControllers. One of those view controllers is the homepage which I called HomeViewController and which is displayed on app load since it occupies index 0 position. What I'm trying to do is create a subview in the viewDidLoad method of MenuTabBarController, but place this subview within HomeViewController. Here's what I came up with
let homeViewController = HomeViewController()//An instance of HomeViewController created at global level
class MenuTabBarController: UITabBarController,UITabBarControllerDelegate,UIPopoverPresentationControllerDelegate{
var mainBox: UIView!//This is the sub view reference declared as an optional
override func viewDidLoad()
super.viewDidLoad()
self.delegate = self
mainBox = UIView(frame: CGRectMake(0,0,200,200))//Initialize mainBox
homeViewController.view.addSubview(mainBox)//Attempt to add mainBox to homeViewController
}//End viewDidLoad
}//End class definition
Well the mainBox doesn't get added because when I attempt to run the app, a blank page stares at me. If I add the mainBox to the menu bar's view like so
self.view.addSubview(mainBox)
it gets added. How can I add it to the homeView though?
You can also do it by accessing index of tabs by getting all controllers of tab bar controller.
(self.viewControllers[0] as! HomeViewController).view.addSubview(mainBox)
Hope it will work for you!!!
You can't add subview to tabbar controller because it's manage all tabs, it's not visible as VC to users. So if you want to ass view in any tac(VC) then you can add it in it's viewdidload method or If you want to add view from tabbarcntoller class then you can get all view controllers in array by calling viewControllers by self. self.viewControllers[0] is you first VC (Home in your case) where self.viewControllers[1] should be second.
Hope this will help :)
It seems that you are creating an extra instance of the home view controller (that is never made visible btw.) and add the sub view into it.
I think what you really want to do is to create and add the sub view in viewDidLoad of your HomeViewController class.