Swift iOS Dynamic Tab bar from json response - swift

I'm new to iOS/Swift. My application is using a json data and I have to create tab bar using the json response. I mean, I get Array of Title from json and I have to create tab bar items based on that array. The Array data/count may change and the app should display the tab bar accordingly. I am trying to create the tab bar programatically without storyboards (as this is huge tab bar)
So far, I have tried the below code -
func tabBarCustom() {
let tt = UITabBarController()
var array1 = [UIViewController]()
var controller1 = UIViewController()
for i in 0..<navgTitle.count {
controller1 = UIViewController(nibName: "WeatherViewController", bundle: nil)
controller1.title = navgTitle[i]
controller1.tabBarItem = UITabBarItem(title: navgTitle[i], image: .none, tag: 1)
array1.append(controller1)
}
print(array1)
tt.viewControllers = array1
self.view.addSubview(tt.view)
}
The above code is failing saying - Could not load NIB in bundle: 'NSBundle' with name 'WeatherViewController'
I am not sure how to create multiple view controllers automatically using the title array, taking title as name of the view controller. is this possible? and how to loop in the array to create view controllers for each tab bar item
please help.Thank you

Are you using nibs? If so, your project can't find the nib file, and you should check out this: Could not load NIB in bundle: 'NSBundle'. Or are you using Storyboard? If you are trying to initialize from the storyboard, you'll do something like this:
let vc = sb.instantiateViewController(withIdentifier: "WeatherViewController")
(just be sure to add WeatherViewController as the view controller's identifier).
OR are you using code? If you are trying to create your view controller from code, you simply need to do WeatherViewController().
As for the tab bar, you're in the right direction it's just that creating the view controller is failing.

Related

UIPageViewController wont refresh UITableViewControllers

Have a controller, we'll call it Bob, as a modal and it contains some labels and a UIPageViewController that loads 2 table controllers. The first time the page controller loads it loads the table controllers with all the correct data. Whenever Bob is opened again the table controllers data does not change. Bob's labels do change so it is receiving different requests from it's parent.
pageController is the name of the UIPageViewController
I've tried doing the following in the datasource section for the pageController
pageController.dataSource = nil
pageController.dataSource = self
but it didn't work. I've also tried setting the controller again after the new data is loaded and that didn't work either
pageController.setViewControllers([viewController(At: 0)!], direction: .forward, animated: true, completion: nil)
In the table controllers they are set to refresh their data like so and this is called after the new data is passed to them. I've verified that this is called whenever Bob is opened.
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
I've also tried recreating the UIPageViewController altogether and that didn't work either.
I'm really stumped as to how to get the page controller to reload the table controllers with the new data. I don't think the issue is at the table controllers but I'm open to trying anything. Anyone have any ideas?
So the cause wasn't any of what I had listed. Go figure. Instead it was how I was defining the table controllers. I was setting the value they where to use for loading when I was defining the controller and not at the time of when I load it.
So
private lazy var thatTableViewController: ThatTableViewController = {
let storyboard = UIStoryboard(name: "ModalsStoryBoard", bundle: Bundle.main)
var viewController = storyboard.instantiateViewController(withIdentifier: "ThatTableViewController") as! ThatTableViewController
viewController.view.tag = 0
return viewController
}()
used to also have a line like so
viewController.controller = Controller(id: self.markerId, activeController: .Problem)
Controller is a custom struct that I was passing. But by setting this at the same level as where the variable thatTableViewController is defined it would not change between calls since it was cached. So moving the
viewController.controller = Controller(id: self.markerId, activeController: .Problem)
outside the definition part, allowed it get changed and not cached.
This then allowed me to remove
pageController.dataSource = nil
pageController.dataSource = self
since the data is defined at the time that I get the controller that I want to load and not predefined.
Hopefully, this will help someone else to not do the same thing I did and set any values you need the other controllers to use to be defined not when you define the controller but later on when you need to use the controller.

Swift - Assign View Controllers dynamically to the Tab bar

My App is all based on API data. I am able to create a tab bar controller from api data but just ended up in confusion on how to create view controllers for each tab bar item dynamically. Here is the code so far -
for i in 0..<navgTitle.count {
print(navgTitle.count)
controller1 = WeatherViewController()
controller1.title = navgTitle[i]
controller1.tabBarItem.title = navgTitle[i]
controller1.tabBarItem.image = UIImage(named: "AppIcon")?.withRenderingMode(.alwaysOriginal)
array1.append(controller1)
}
print(array1)
tt.viewControllers = array1
self.view.addSubview(tt.view)
self.tabBarControllr.moreNavigationController.show(tt, sender: self)
tt.customizableViewControllers = []
}
In the above code, in the 'for' loop i want to create view controllers for each tab bar item (say 'HomeViewController', WeatherViewController' etc). I do not know how to do that and is it really possible? I can not create view controller classes manually because each app has their own tab bar items to show. Please suggest a way to do this..Thanks in Advance

Swift -injecting vars in ViewControllers

I am proudly new to iOS developing and I am trying to build my first app. I am doing a course on an online platform which does the following in the
AppDelegate -> application didFinishLaunchingWithOptions:
let navigationController = window?.rootViewController as! UINavigationController
let notebooksListViewController = navigationController.topViewController as! NotebooksListViewController
notebooksListViewController.dataController = dataController
This app has a Navigation controller which begins with an UIViewController.
I have 2 questions here, first is why this works, I mean, I am in AppDelegate, so the NotebooksListViewController (first view of the app) is not instantiated yet (I think), so why I am able to inject a variable in it?
On the other hand, the second question, is how can I do this in a different scene? I have a TabBarViewController as first scene, and the first tab is a UITableViewController and I want to inject the same way my dataController var, how can I accomplish this? I could not get to do it, neither understand it.
Thanks in advance.
It works, because of some Xcode magic:
In your Target Setting, General tab, the Main Interface entry specifies the name of the Storyboard to be loaded automatically when your app starts up:
In the storyboard, the Initial View Controller then will be instantiated. It seems like this is an UINavigationController.
Since this is done automatically, it just works - until you want to do something special :-)
If you want to start up with a different scene - maybe from a different view controller - you could just change either the Main Interface to another storyboard, the Initial View Controller (inside the storyboard) or both.
Or, you could just start up by yourself, by leaving the Main Interface empty and create your own view controller inside the app delegate (didFinishLaunchingWithOptions), something like
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let tabVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabCtrl") as? UITabBarController {
self.window?.rootViewController = tabVC
// Access the subcontrollers, or create them
// Initialize their values
// tabVC.viewControllers[0].data = ...
} else {
// Ooops
}
self.window?.makeKeyAndVisible()
Answer to your first question
as the method name is self explanatory didFinishLaunchingWithOptions means your application is didfinish with launching with options and its about to enter in foreground so here application need to set rootViewController so in this method controller you want to set as view controller is initiated thats why you can inject variable in it
answer to second question
let navigationController = window?.rootViewController as! UITabbarController
let VC = navigationController.childViewController
//Now Using VC you can access all you controller of tabbar controller
let notebooksListViewController = navigationController.topViewController as!
NotebooksListViewController
notebooksListViewController.dataController = dataController
now as shown above you can use VC to access you view controllers
but be careful here because VC return viewcontroller array so you need make checks for perticular VC you want to access

Opening window + view from an other view controller

I've got a ViewControllerOne. ViewControllerOne is connected via Ctrl-Drag (in storyboard) to a menu-button mBtn (which means I don't know how it is implemented programmatically).
Clicking on this mBtn, a ViewOne appears (present modal). This ViewOne is bound to ViewControllerOne. ViewOne has a button btnOne.
Clicking on btnOne I want ViewOne to be dismissed and ViewTwo to be shown. ViewTwo belongs to ViewControllerTwo and to WindowControllerTwo.
The WindowControllerTwo-ViewControllerTwo-binding is the standard case as created on a new project.
I have the following code in the IBAction for button btnOne in ViewControllerOne:
#IBAction func onbtnOnePressed(sender: AnyObject){
let m_WindowControllerTwo = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("WindowControllerTwo")) as! NSWindowController // I have no custom class for the window controller as I don't really know what I can use it for ...
let m_ViewTwo = WindowControllerTwo.contentViewController as! ViewControllerTwo // my custom class for ViewTwo
m_ViewTwo.attributeIWantToPassToThisView = self.x // an attribute I want to pass from view a to view b
m_WindowControllerTwo.contentViewController = m_ViewTwo // passing the attribute from a to b
m_WindowControllerTwo.showWindow(self) // this does not work
self.dismiss(nil) // see NOTE
}
This code actually does not work. On debugging it step by step, I'm seeing the window/view flickering but not appearing...
NOTE: I could connect the button btnOne with a ctrl-drag to ViewControllerTwo. This works. But then the current ViewOne does not get dismissed!
Question: What am I doing wrong here? In iOS swift this also works. I don't quite get the WindowController stuff, so I'll need your advice on this.
Instead of this: m_WindowControllerTwo.showWindow(self)
use:
let application = NSApplication.shared()
application.runModal(for: wordCountWindow) //this will present WindowControllerTwo modally.
then to close your present controller add this line: PresentWindowControllerName.close()

How to make tab bar controller update?

I'm rather new to swift and I am currently building an application that shows movies and TV shows that are in theatres / just airing.
Initially I had it so that there are only 2 tabs in the tab bar controller and it shows newest movies and recently aired TV shows. In order to enhance user experience I created a menu through which the use could pick up to 4 different categories (e.g. Top rated movies, newly released movies, popular movies and so on).
I have created an array so that the tab bar controller knows where to get the data from to see the movies and the TV shows. When the user selects the new categories, the array is updated, but the tab bar controller does not update its tabs. When the app is restarted though, the tabs reflect the newly selected categories.
I know if this were a tableview I could call tableview.reload() but I'm not sure how to do this for a tab bar controller. Please help me out and thanks in advance!
Here is the link to my project
Kevin you can make one function in appDelegate and make the tabbar a rootview controller
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nowPlayingNavigationController = storyboard.instantiateViewControllerWithIdentifier("FlicksNavigationController") as! UINavigationController
let nowPlayingViewController = nowPlayingNavigationController.topViewController as! MoviesViewController
nowPlayingViewController.endpoint = "now_playing"
nowPlayingNavigationController.tabBarItem.title = "YOUR SELECTED TITLE"
nowPlayingNavigationController.tabBarItem.image = UIImage(named: "popular")
nowPlayingNavigationController.navigationBar.barStyle = .BlackTranslucent
let topRatedNavigationController = storyboard.instantiateViewControllerWithIdentifier("FlicksNavigationController") as! UINavigationController
let topRatedViewController = topRatedNavigationController.topViewController as! MoviesViewController
topRatedViewController.endpoint = "top_rated"
topRatedNavigationController.tabBarItem.title = "YOUR SELECTED TITLE"
topRatedNavigationController.tabBarItem.image = UIImage(named: "topRated")
topRatedNavigationController.navigationBar.barStyle = .BlackTranslucent
window?.rootViewController = tabBarController
You can write this code in that function and make it root viewcontroller and make the title change,
Or
in your Tabbarviewcontroller.swift you can use postnotification and update it accordingly.