I am new to swift programming and I have a Tabbed application with 2 UITabBar items , I want that when user taping the second tab, application set the selected tab index to 0 and move user to the first tab. I tried this code in my SecondViewController.swift's viewDidLoad :
self.tabBarController?.selectedIndex = 0
but it doesn't work.
This works:
override func viewDidAppear() {
self.tabBarController?.selectedIndex = 0
}
See this answer for a good explanation of viewDidLoad() vs. viewDidAppear.
Relevant excerpt:
This is where you want to perform any layout actions or do any drawing in the UI - for example, presenting a modal view controller
Related
I'm using XCode 13.0, Swift 5.5 and Storyboard. This is a mobile app for iPhone with iOS 15.
I have altogether 7 tabs, all of them with icons. I have 4 tabs and a "More" tab in the tab bar of my app.
3 additional tabs show up after the user clicks on the "More" tab. I'd like the first 2 of these additional tabs to be disabled. They need to be grey coloured and when the user clicks on any of these 2, I'd like to app to do nothing (not to show any page, not to navigate anywhere). I'm using the original UITabBarController and moreNavigationController defined by Apple, I didn't subclass any of them.
On my storyboard I added 2 UIViewControllers to these 2 disabled tabs, but I set them disabled this way:
This didn't work at all. When the user clicks on the 2 disabled tabs, they show 2 empty ViewControllers. I'd expect them not to show anything.
I also tried accessing the moreNavigationController's tabs from the page my UITabBarController first opens. I tried setting these 2 tabs disabled programmatically. However I didn't manage to access these 2 tabs, I only manage to access the main tabs that show up in the tab bar (I don't need to access these). How can I disable and colour grey the tabs that show up after clicking on the More tab?
let moreControllerItems = tabBarController?.moreNavigationController.toolbarItems
if let tabArray = moreControllerItems {
let tabBarItem1 = tabArray[0]
let tabBarItem2 = tabArray[1]
tabBarItem1.isEnabled = false
tabBarItem2.isEnabled = false
}`
This code doesn't work, the moreControllerItems variable is nil. The tabBarController variable isn't nil, I can access my UITabBarController from here, but it doesn't help me much.
I googled this issue lots of different ways but I couldn't find the solution. Any help would be appreciated. I really need to solve this, please write a comment if you have any idea how to solve this. Thank you!
You can disable tabBar items from TabBarController.
class TabVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
}
func disableTabBar(itemNo: Int) {
if let items = tabBar.items, itemNo < items.count {
items[itemNo].isEnabled = false
}
}
And you can access this function from
any child Viewcontroller attached to TabBarController
class MoreVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let tabBarVC = tabBarController as? TabVC {
tabBarVC.disableTabBar(itemNo: 1)
}
}
In short: You cannot disable it. If you want to disable the item in the More Viewcontroller, you have to use a custom Viewcontroller in the fifth Tab
I am explaining here why we cannot do it and what limitations we have.
Apple will show the More tab if items are more than five in the UITabBarController. And more tab holds the UINavigationController which is attached to a kind of UIMoreListController. you can check by
print moreNavigationController.viewControllers[0]
UIMoreListController is not accessible. In your case, the remaining three items are listed in the UIMoreListController. Although we can still access the Viewcontroller's tabBarItem and we can disable it. You can check by clicking the edit option.
But still, users can click on it as long as it is in the UIMoreListController. Because we are not interacting with the tabBar instead we are interacting with the items on the list. Users can't click once you move the item to any of the first four positions using the edit option in the MoreViewcontroller.
I have built a simple tab bar with 3 tabs.
I want to run a UI test to make sure that if the user clicks a tab bar item, the correct view controller shows. How would I go about doing that? Below is the code I would start with, just don't know how to write my assertion.
func testTabBarMyProfileButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["My Profile"].tap()
}
func testTabBarGraphsButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["Graphs"].tap()
}
func testTabBarAboutButton() {
let tabBarsQuery = XCUIApplication().tabBars
tabBarsQuery.buttons["About"].tap()
}
You can access the tabbar button by its position:
app.tabBars.buttons.element(boundBy: 2).tap()
If you have different controls in each view controller shown on each tab bar, you can make assertions if they exist or not (what is expected).
For example if the first tab bar has UILabel named "First name" you can assert if it exists by writing
Let theLabel = app.staticTexts["myValue"]
XCTAssert(theLabel.exists).to(beTrue)
And on the other screens do the same thing for the different controls.
If anyone finds this looking to UI test the contents of another app, I just found a solution..
The tab bar item is a lazy variable and needs to be touched before you can reference a tab bar button by value. Add this line:
tabBarItem.accessibilityIdentifier = "my-snazzy-identifier"
to the viewDidLoad method and you should be able to do this in your UI tests:
app.tabBars.buttons["Button Title"].tap()
You can test the title of the navigation bar.
XCTAssert(app.navigationBars["Graphs"].exists)
See my GitHub repo for a more detailed UI Testing example.
I am currently implementing the XLPagerTabStrip (https://github.com/xmartlabs/XLPagerTabStrip) which effectively creates a tab bar at the top of the view controller. I want to be able to segue to a new view controller from one of the tabbed controllers and be able to use the navigation bar to move backwards (or a custom version of the navigation bar if this isn't possible).
XLPagerTabStrip provides the moveToViewController and moveToViewControllerAtIndex functions to navigate between child view controllers, but this method doesn't allow use of a navigation bar to go backwards.
Conceptually XLPagerTabStrip is a collection of view controllers declared and initialized during the XLPagerTabStrip model creation.
It has virtually no sense to use a UINavigationController if you already have all the viewcontrollers available.
You can create a global var previousIndex to store the previous viewController index and allow users to go back by using canonical methods:
func moveToViewControllerAtIndex(index: Int)
func moveToViewControllerAtIndex(index: Int, animated: Bool)
func moveToViewController(viewController: UIViewController)
func moveToViewController(viewController: UIViewController, animated: Bool)
About a new viewController, suppose you have 4 viewControllers that built your container (XLPagerTabStrip) named for example z1, z2, z3 e z4.
You can embed to z4 a UINavigationController (so it have the z4 controller as rootViewController) and start to push or pop your external views. When you want to return to your z4 you can do popToRootViewControllerAnimated to your UINavigationController
When you are go back to z4 , here you can handle your global var previousIndex to moving inside XLPagerTabStrip.
I'm not familiar with XLPagerTabStrip, but I had a similar problem recently and the solution was to use an unwind segue to go back to the previous view controller. It's pretty trivial to implement so probably worth a try.
To navigate back to your previous view tab controller, you had initially navigated from;
Embed your new view controller, from which you wish to navigate
away from in a navigation bar
Connect it's Navigation Bar Button to the Parent view containing the
tab bar by dragging a segue between the 2 views
Create a global variable in App delegate to store current index
which you will use in the Parent view to determine what tab view
controller to be shown
var previousIndex: Int = 0 //0 being a random tab index I have chosen
In your new view controller's (the one you wish to segue from)
viewdidload function, create an instance of your global variable as
shown below and assign a value to represent a representative index
of the child tab bar view controller which houses it.
//Global variable instance to set tab index on segue
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.previousIndex = 2
You can write this for as many child-tab connected views as you wish, remembering to set the appropriate child-tab index you wish to segue back to
Now, create a class property to reference your global variable and a function in your Parent view as shown below
let appDelegatefetch = UIApplication.shared.delegate as! AppDelegate
The function
func moveToViewControllerAtIndex(){
if (appDelegatefetch.previousIndex == 1){
self.moveToViewControllerAtIndex((self.appDelegatefetch.previousIndex), animated: false)
} else if (appDelegatefetch.previousIndex == 2){
self.moveToViewControllerAtIndex((self.appDelegatefetch.previousIndex), animated: false)
}
}
You may now call this function in the Parent View Controller's viewDidLoad, as shown below.
moveToViewControllerAtIndex()
Run your project and that's it.
I'm working for a IOS9+ project using swift ,
using the same slide menu (Which is done programmatically) to translate between view controllers as shown in the link : Slide Menu and it working fine
the problem is it's using storyboard ID for each view controller in the function addChildView():
func addChildView(storyBoardID: String, titleOfChildren: String, iconName: String) {
let childViewToAdd: UIViewController = storyboard!.instantiateViewControllerWithIdentifier(storyBoardID)
tabOfChildViewController += [childViewToAdd]
tabOfChildViewControllerName += [titleOfChildren]
tabOfChildViewControllerIconName += [iconName]
menuToReturn.append(["title":titleOfChildren, "icon":iconName])
}
which is called in TransitionBetweenTwoViews function :
func transitionBetweenTwoViews(subViewNew: UIViewController){
//Add new view
addChildViewController(subViewNew)
subViewNew.view.frame = (self.parentViewController?.view.frame)!
view.addSubview(subViewNew.view)
subViewNew.didMoveToParentViewController(self)
//Remove old view
if self.childViewControllers.count > 1 {
//Remove old view
let oldViewController: UIViewController = self.childViewControllers.first!
oldViewController.willMoveToParentViewController(nil)
oldViewController.view.removeFromSuperview()
oldViewController.removeFromParentViewController()
}
print("After Remove: \(self.childViewControllers.description)")
}
that is called in the delegate of the BaseViewController Class
My problem is that I'm not using storyboard instead a nib file for each view controller , how could i do it without storyboard I have been searching for days to find solution but nothing work
Also the tableView of the menu is a table with sections
Please if you have any solution that could help it will be appreciated
or any other slide menu that work with nib files
Edit :
Slide Menu table View :
screen shot of slide menu with section so if i choose Flight Availability or Ticket Balance it will be like i choose home with index 0 how could i solve that ?
I have a UITabBarController with 5 NavigationControllers, embedded in it as tabs (similar to picture 1 below). So, the question is: I want to show the table view (or any another view) as a "default" view, when the TabBarController is shown. I.e., show the view (view controller), which is not embedded in TabBarController and make any tab selected. I apologize for such explanation, better watch on picture #2 below. I'm using latest version of XCode and Swift in my project. And for interface I'm using storyboard
You can determine which tab to select in the first (initial) viewController.
Note: Swift 3
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.selectedIndex = 1
}
Note that by default the first selected viewController is at 0 index (self.tabBarController?.selectedIndex = 0)