3

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.

amagain
  • 1,936
  • 2
  • 16
  • 32

3 Answers3

3

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

IvanMih
  • 1,525
  • 1
  • 9
  • 20
0

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)
     }
}
Ram Madhavan
  • 2,166
  • 1
  • 15
  • 20
-1

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
}
YinKiet
  • 429
  • 6
  • 13
  • Tried and tested this as well, gives fatalError("viewControllers(for:) should provide at least one child view controller") error – amagain Oct 16 '17 at 09:51
  • this is due to "childrenVC" is empty, please make sure "postCategories" is not empty – YinKiet Oct 16 '17 at 10:07