Disable All buttons except Home in Tab Controller - swift

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.

Related

Check if Popped UIViewController gets dismissed by swipe

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.

How can I check popularProduct.xib file loaded into HomeViewController

In my HomeViewController have four section:
Banner
Popular Product
New Product
Old Product
All are .xib files and they are working fine.
Now I want to add ActivityIndicator in my HomeViewController
So now I want to show ActivityIndicator on HomeViewController until all the .xib's file not fully loaded in HomeViewController as .xib's ActivityIndicator should hide.
Now, the problem for me is that how can I get the confirmation about .xib's loaded to HomeViewController or not?
As a pretty simple direct solution, you could follow the delegation approach. So, when "Popular Product" View complete the needed process, you should fire a delegate method which will be handled by the view controller.
Example:
consider that in PopularProduct class you are implementing a method called doSomething() which need to get called and finish its work to hide the activity indicator from the view controller and it should send Data instance to the view controller. You could do it like this:
protocol PopularProductDelegate: class {
func popularProduct(popularProduct: PopularProduct, doSomethingDidPerformWith data: Data)
}
class PopularProduct: UIView {
// ...
weak var delegate: PopularProductDelegate?
func doSomething() {
// consider that there is much work to be done here
// which generates a data instance:
let data = Data(base64Encoded: "...")!
// therefore:
delegate?.popularProduct(popularProduct: self, doSomethingDidPerformWith: data)
}
// ...
}
Therefore, in ViewController:
class ViewController: UIViewController {
// ...
var popularProduct: PopularProduct!
override func viewDidLoad() {
super.viewDidLoad()
// show indicator
popularProduct.doSomething()
}
// ...
}
extension ViewController: PopularProductDelegate {
func popularProduct(popularProduct: PopularProduct, doSomethingDidPerformWith data: Data) {
print(data)
// hide indicator
}
}
Furthermore, you could check my answer for better understanding for delegates.

how to segue to storyboard viewcontroller from xib view with swift 3

I'm having the hardest time finding an answer for this.
I have a xib view that is within a scrollview that is within a view controller. In the xib I have a button with an action and I need to segue to a view controller I have in my storyboard. I also would like to be able to use a custom segue.
So far, I have read that I can instantiate the viewcontroller from the storyboard to segue to it. But then I don't know how to present that controller.
thanks for any help...
UPDATE:
this is the code I'm using to perform the segue.
In parent ViewController:
static var referenceVC: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
print("viewdidload")
LevelSelectViewController.referenceVC = self
setupScrollView()
}
code in xib view file
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sightWordController")
let parent = LevelSelectViewController.referenceVC!
let segue = InFromRightCustomSegue(identifier: "test", source: parent, destination: vc)
segue.perform()
As noted in the comments, Segues are typically confined to storyboard usage as noted in the documentation. You can implement a custom xib view in a storyboard via #IBDesignable like approaches and have you're view load from the xib into the storyboard file/class. This way, you gain the benefits of both worlds. Otherwise, you may want to approach this in another fashion (such as delegates/target-action events, etc).
You may also climb the responder chain and call a segue related to the VC loaded from the storyboard (the segue doesn't necessarily have to be attached to any particular action) via getting a reference to the VC and calling the segue. You can climb the responder chain in a manner such as the example code below:
protocol ChildViewControllerContainer {
var parentViewController: UIViewController? { get }
}
protocol ViewControllerTraversable {
func viewController<T: UIViewController>() -> T?
}
extension UIView: ViewControllerTraversable {
func viewController<T: UIViewController>() -> T? {
var responder = next
while let currentResponder = responder {
guard responder is T else {
responder = currentResponder.next
continue
}
break
}
return responder as? T
}
}
extension UITableViewCell: ChildViewControllerContainer {
weak var parentViewController: UIViewController? {
return viewController() as UIViewController?
}
}

Call a method from anywhere in a tabbar application swift

I have a tabBar application with 4 different ViewControllers.
Is it possible to call the same method from all the 4 views, where do I have to put the method?
For the record it is just a call to show a iAd banner.
Thanks
This would work. Create a custom subclass of UITabBarController. Put your function in there. Be sure the change the class of the TabBarController in the Storyboard to CustomTabBarController.
class CustomTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func myFunctionToCallFromAnywhere() {
print("Hey, it works")
}
}
Then in your viewControllers that are managed by your TabBarController you can call the function like this:
(self.tabBarController as? CustomTabBarController)?.myFunctionToCallFromAnywhere()

How to stop loading the view on tabbar item selection?

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;