I am using XLPagerTabStrip
to create a category based reading app in Swift 4. I learnt static number of ViewControllers can be easily created using the following function.
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] { }
However, the number of categories in my case depends on server response, which may change as per the need. I tried to create dynamic number of tabs by creating view controllers based on the name of categories I parsed from the json response. This is the method I did a hit and trial.
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var childrenVC = [UIViewController]()
for eachCategory in postCategories {
print(eachCategory)
let newVC = self.storyboard?.instantiateViewController(withIdentifier: "FirstTVC") as? FirstTVC
newVC?.childName = eachCategory.name
childrenVC.append(newVC!)
self.reloadPagerTabStripView()
self.reloadInputViews()
}
return childrenVC
}
Yes, it failed. How can I achieve dynamic number of tabs in this case? If not I am also open to any other alternative. I am done with json response thing but stuck in this step. This SO answer and Github Issue didn't help as well.
I had the exact same situation. If you fetch results from the server asynchronously, as you should, you will have a crash since xlpagertabstrip needs at least one child viewcontroller.
The solution i found is to return one dummy viewcontroller at the start of the application, and after fetching data from the server to reloadPagerTabStripView.
In your parent viewcontroller you should make an empty array of your objects which you fetch from the server, for example:
var menuItems = [MenuObject]()
Next, viewControllers(for pagerTabStripController ... ) method should look like this:
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var children = [UIViewController]()
if menuItems.count == 0 {
let child = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DummyVC") as! DummyVC
children.append(child)
return children
} else {
let menuItemsUrls = menuItems.map{$0.url}
for menuItem in menuItems {
let child = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainVC") as! TelegrafMainVC
child.name = menuItem.name.uppercased
child.url = menuItem.url
children.append(child)
}
return children
}
}
And after you fetch data from the server, whether using URLSession, or Alamofire, or whatever else, in the function or a closure you should put something like:
self.menuItems = menuItems
DispatchQueue.main.async {
self.reloadPagerTabStripView()
}
Tell me if i need to clarify something, but i believe this will be sufficient to help you.
Cheers
Try this
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
var childrenVC: [UIViewController] = []
for eachCategory in postCategories {
let newVC = UIStoryboard(name: "Main (YOUR-STORYBOARD-NAME)", bundle: nil).instantiateViewController(withIdentifier: "FirstTVC") as? FirstTVC
newVC?.childName = eachCategory.name
childrenVC.append(newVC!)
}
return childrenVC
}
Step 1 : Do this in your initial viewcontroller,
class initialViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
self.loadMainView(viewControllerArray: self.setMainViewTabParameters())
}
func setMainViewTabParameters() -> NSMutableArray {
let viewControllerArray = NSMutableArray()
var tabArray = ["Tab1", "Tab2", "Tab3", "Tab4", "Tab5", "Tab6", "Tab7"]
var tabIndex = NSInteger()
for item in tabArray {
let tabString = tabArray[tabIndex]
let tabview = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "YourTabControllerIdentifier") as! YourTabController
tabview.tabHeadingTitle = tabString as NSString
viewControllerArray.add(tabview)
tabIndex = tabIndex + 1
}
return viewControllerArray
}
func loadMainView(viewControllerArray : NSMutableArray) -> Void {
let mainView = self.storyboard?.instantiateViewController(withIdentifier: "YourMainControllerIdentifier") as! YourMainController
mainView.viewControllerList = viewControllerArray
self.navigationController?.pushViewController(mainView, animated: false)
}
}
Step 2:
class YourMainController: ButtonBarPagerTabStripViewController {
var viewControllerList = NSArray()
var isReload = false
//MARK: - PagerTabStripDataSource
override func viewControllers(for pagerTabStripController: PagerTabStripViewController) -> [UIViewController] {
guard isReload else {
return viewControllerList as! [UIViewController]
}
var childViewControllers = viewControllerList as! [UIViewController]
for (index, _) in childViewControllers.enumerated(){
let nElements = childViewControllers.count - index
let n = (Int(arc4random()) % nElements) + index
if n != index{
swap(&childViewControllers[index], &childViewControllers[n])
}
}
let nItems = 1 + (arc4random() % 8)
return Array(childViewControllers.prefix(Int(nItems)))
}
override func reloadPagerTabStripView() {
isReload = true
if arc4random() % 2 == 0 {
pagerBehaviour = .progressive(skipIntermediateViewControllers: arc4random() % 2 == 0, elasticIndicatorLimit: arc4random() % 2 == 0 )
} else {
pagerBehaviour = .common(skipIntermediateViewControllers: arc4random() % 2 == 0)
}
super.reloadPagerTabStripView()
}
}
Step 3: Your tab view controller:
class YourTabController: UIViewController, IndicatorInfoProvider {
var tabHeadingTitle = NSString()
// MARK: - Top Tab Bar Method - IndicatorInfoProvider
func indicatorInfo(for pagerTabStripController: PagerTabStripViewController) -> IndicatorInfo {
return IndicatorInfo(title: tabHeadingTitle as String)
}
}