I have a tabbar controller with three tabs . In first tab I have a navigation controller . Now User navigates in the first tab to do some payment so I have disabled the default back buttons cause I dont want user to use back button in between transaction. But when user presses the tab again he/she navigates to the root view . How can I detect the tabbar selection or how do I avoid loading the tab again ?
Please help me on this !! Thank You !!
Note: I am not sure if my question has been already answered on stackoverflow in some other post but I did search and did not get any answer . If so , please feel free to redirect me to that answer and delete this post . Thanx !
Check the UITabBarControllerDelegate Protocol Reference.
The basic idea is that the tabBarController:shouldSelectViewController: selector in your UITabBarController delegate is called whenever the user clicks on tab item.
Thus, by appropriately defining that method, you get a chance to do your own processing before the current view controller is replaced by the one the user selected by clicking in the tab bar.
So, simply return NO from this selector in case you wish to prevent the current view controller to be replaced, i.e. when a transaction is ongoing.
In this way you can open any controller or perform any action on the selection of specific index.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
let selectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController)!
if selectedIndex == 1{
//Do any thing.
return false
}
return true
}
You have to do it this way..
- (BOOL)tabBarController:(UITabBarController *)tbc shouldSelectViewController:(UIViewController *)vc
{
UIViewController *tbSelectedController = tbc.selectedViewController;
if ([tbSelectedController isEqual:vc])
{
return NO;
}
return YES;
}
In Swift 5:
Continuing Talha Rasool's answer, don't forget to delegate self in viewDidLoad function. This will let current MainTabBarController to handle all the delegate methods
import UIKit
class MainTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self // Delegate self to handle delegate methods.
}
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
let selectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController)!
if selectedIndex == 1{
//Do anything.
return false
}
return true
}
}
When user navigate to payment controller , you can hide the tabbar.
Use this code in your code , when you navigate to another view
yourcontroller.hidebottombarwhenpushed=YES;
Related
I want to check whenever the user swipes a popped viewController away. So for example when in whatsApp a user exits the current chat by swiping from the edge. How is that possible in Swift?
I don't want to use viewDidDisappear, because this method also gets called when another viewController is presented over the current viewController.
As I wrote in comment, a simple workaround would be in viewDidDisappear, check if the navigationController is nil.
class MyVc: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if navigationController == nil {
print("view controller has been popped")
}
}
}
Of course, this solution works only if the view controller is embedded into a navigation controller, otherwise the if statement will always be true.
This "swipe" is handled by the interactivePopGestureRecognizer of the UINavigationController. It is possible to set the delegate of this gesture recognizer to your UIViewController as follows:
navigationController?.interactivePopGestureRecognizer?.delegate = self
Then, you can implement the UIGestureRecognizerDelegate in your UIViewController. This would look like this:
extension YourViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard gestureRecognizer.isEqual(self.navigationController?.interactivePopGestureRecognizer) else { return true }
print("The interactive pop gesture recognizer is being called.")
return true
}
}
I haven't tested the code, but this should print every time the interactivePopGestureRecognizer is used.
For more information, refer to the documentation of the interactivePopGestureRecognizer and UIGestureRecognizerDelegate.
I am using sheet presentation to view settings view iOS13.when the user dismisses or closes the view I want to trigger reload to previews viewController to update the view.
I tried viewWillAppear in previews ViewController but No user
note even viewDidLoad
how to force previews viewController in sheet presentation?
Override the dismiss function in your settings View Controller and write a delegate to send the reload action.
Assign the delegate to the view controller that you want to send the reload information.
protocol MyViewControllerDelegate: class {
func myViewControllerDidDismiss()
}
class MyViewController: UIViewController {
weak var delegate: MyViewControllerDelegate?
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
self.delegate?.myViewControllerDidDismiss()
super.dismiss(animated: flag, completion: completion)
}
}
Other way is making the protocol and overriding viewDidDisappear and send the delegate method when the settings view controller execute the viewDidDisappear. Feel free!
Ive been searching and i cant seem to find out how to disable all tab bar items EXCEPT the home button from being used until a user logs in or creates and account
You can do something like below, create a custom class(TabBarController), extend it from UITabBarController, and write code inside TabBarController class.
Assign TabBarController class to your UITabBarController
extension TabBarController: UITabBarControllerDelegate{
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
// allow your desired controller to be tapped
if tabBarController.selectedIndex == indexOfHomeControllerInTabBar {
return true
}
return false
}
}
Note: Apple doesn't recommend blocking tabbars, for more info check this link https://developer.apple.com/design/human-interface-guidelines/ios/bars/tab-bars/
here is my approach
note that it is kinda a hack way so you might modify as per your needs
you will conform to UITabBarControllerDelegate and make your VC the delegate for it in the viewDidLoad
then in the "didSelect viewController" delegate method callback you will do your logic and override the selected index as below code
class ViewController: UIViewController, UITabBarControllerDelegate {
// MARK: Lifecycle Methods
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if /* your logged in logic */ {
self.tabBarController?.selectedIndex = 0 /* assuming that the home is at index 0 */
}
}
}
note if you did this step in base VC it will be much better and code saving for you
You can loop for all items in your Tabbar and disable items you want
for i in 0..<tabbarController.tabBar.items!.count {
let item = tabbarController.tabBar.items![i]
item.isEnabled = i == indexOfHomeTab
}
Put this somewhere in viewDidLoad()
if let viewControllers = self.tabBarController?.viewControllers {
for viewController in viewControllers {
if viewController != viewControllers[0] { // assuming your homeViewController index is 0
tabBarController?.tabBarItem.isEnabled = false
}
}
}
The short answer is probably "Don't do that." Tabs in a tab bar are supposed to let the user switch between always-available screen at the top level of your UI. If you read Apple's HIG I suspect you will find that what you are trying to do is not recommended.
Better to have each of the other screens show some sort of disabled/inactive state.
I connected UITableViews with Show segue. I want to change some variable of destination tableView whenever I tap the Back button on navigation bar. Let's say I'm not allowed to create a new back button, how can I detect/call the destination tableView when the back button is tapped? Like in prepareforsegue we have segue.destination, is there some thing like "backbutton.destination"?
Thanks!
What you should be doing here (I think) is not trying to find the destination of the back button but passing in the interested object when going forwards.
On you detail view you could have something like...
protocol DetailViewDelegate {
func detailViewDidChange()
}
Then in the detail view controller...
class DetailViewController: UIViewController {
weak var delegate: DetailViewDelegate?
// you could call this from viewWillDisappear or something
func doThisWhenYouWantToUpdateTheTableView() {
delegate?.detailViewDidChange()
}
}
Now in the tableview...
func prepareForSegue(...) {
if let vc = segue.destination as? DetailViewController {
vc.delegate = self
}
}
and...
extension MyTableViewController: DetailViewDelegate {
func detailViewDidChange() {
// respond to the change here.
}
}
I've got a tab bar application and I need to know when and what button a user taps on the tab bar as to display the appropriate notifications and such.
In short: How would I go about detecting the index of a tapped UITabBarItem on a UITabBar?
Thanks in advance!
The answer depends on whether or not the UITabBar is managed by a UITabBarController or not.
Case 1 - UITabBar is already handled by a UITabBarController
Implement the UITabBarControllerDelegate protocol. Specifically the tabBarContoller:didSelectViewController: method. Set an instance of your class that implements the protocol as the delegate of the UITabBarController.
- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger indexOfTab = [theTabBarController.viewControllers indexOfObject:viewController];
NSLog(#"Tab index = %u (%u)", (int)indexOfTab);
}
In this case you have to be aware of the special situation where you have enough controllers in the tab controller to cause the "More" tab to be displayed. In that case you'll receive a call to the tabBarController:didSelectViewController: with a view controller that isn't in the list (it's an instance of an internal UIKit class UIMoreNavigationController). In that case the indexOfTab in my sample will be NSNotFound.
Case 2 - UITabBar is NOT already handled by a UITabBarController
Implement the UITabBarDelegate protocol. Specifically the tabBar:didSelectItem: method. Set an instance of your class that implements the protocol as the delegate of the UITabBar.
- (void)tabBar:(UITabBar *)theTabBar didSelectItem:(UITabBarItem *)item {
NSUInteger indexOfTab = [[theTabBar items] indexOfObject:item];
NSLog(#"Tab index = %u", (int)indexOfTab);
}
EDIT: Modified the method parameter variables to eliminate the OP's compilation warning about tabBarController being hidden.
SWIFT:
// somewhere inside your TabBarViewController
//...
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let indexOfTab = tabBar.items?.index(of: item)
print("pressed tabBar: \(String(describing: indexOfTab))")
}
SWIFT 4:
I prefer
// somewhere inside your TabBarViewController
//...
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool{
let index = tabBarController.viewControllers?.index(of: viewController)
return true// you decide
}
There's a method defined in the UITabBarDelegate protocol called tabBar:didSelectItem:, which will notify you of which and when a UITabBarItem is selected (tapped).
I did it like this :
This is in a custom class which extends UITabBarController
.h
#interface CustomTabBarController : UITabBarController<UITabBarDelegate>
.m
-(void)tabBar:(UITabBar *)theTabBar didSelectItem:(UIViewController *)viewController
{
NSLog(#"Tab index = %# ", theTabBar.selectedItem);
for(int i = 0; i < theTabBar.items.count; i++)
{
if(theTabBar.selectedItem == theTabBar.items[i])
{
NSLog(#"%d",i);// this will give the selected tab
}
}
//NSlog(#"Items = %#", theTabBar.items[0]);
}
Simple extension for Swift 4:
extension UITabBarController {
func getSelectedTabIndex() -> Int? {
if let selectedItem = self.tabBar.selectedItem {
return self.tabBar.items?.firstIndex(of: selectedItem)
}
return nil
}
}
And usage:
let index = getSelectedTabIndex()