I'm using a cocoa pod called ACTabScrollView. In the demo code, it has a View Controller for the UI part which scrolls from side to side and has different tabs, and a view controller that has a table view (a "content view controller"). Each tab displays different information on each table view, but the information is sourced from a single array and is divided via an assigned enum. If this is confusing, there is a gif on the ACTabScrollView readme. In my "content view controller," I can hard code an array and it works flawlessly. The class for this content view controller has a variable declaration that I've never seen before as I'm new to iOS programming.
var category: TeamCategory? {
didSet {
for team in Team.teamArray {
if (team.category == category || category == .all) {
teamArray.append(team)
}
}
}
}
A project sample with this snippet is located here.
In the above snippet, Team is a model of teams, and the class has a hard coded array of teams (which are structs).
Obviously, I don't want to hard code the array of Teams in the app because I want to be able to update the array and have it update in real time. I'm using Firebase and I've tried loading the array in the App Delegate, in the UI view controller, and in the content view controller, but the tableView does not populate. I've confirmed that the data is arriving.
When/Where should I load the data from Firebase? The content view controller needs the array loaded before the view controller is loaded. Any tips for this would be much appreciated.
When you should load the data from Firebase is a design decision. You said that the content view controller needs to have the array loaded before the view controller is loaded, so you should request that data from Firebase at some point in your app flow where you can be guaranteed that the request will have come back with data before your TableView is attempting to load that data.
You may be running into a race condition where you are requesting the data, and then going to the content view controller before the request finished.
In general you don't want to have to rely on a guarantee that a request will finish in time, so I recommend doing what #rmaddy suggested, where you load the data before hand, and then have some sort of completion handler / notification / delegate call that will call reloadData on your tableView once the array from which you are pulling your data is updated.
The project sample you are referring to does not handle an asynchronous case, as you are handling. The didSet in the property declaration is just a closure that gets executed when you set a value for that property. In the case of the project sample, the news category is set at the time of the view controller instantiation, and then the didSet executes once and attaches the appropriate news array for the tableView to access. Again, in your case, as long as you are updating your array that contains your tableview's source of data after the data returns from Firebase, calling reloadData on your tableView should achieve the result you are looking for. If it doesn't, then that means you aren't properly updating that teamArray.
Related
Where do you usually load data when using a UIViewController?
When I say "loading data" I mean: API calls to fetch it and its manipulation.
Do you load it in the view controller initializer, or load it before to initialize it and pass it? Or do you add the code to viewDidLoad?
Also, what about the data for the rootViewController? The one that initialises from storyboard when you launch the app?
Do you load it in the view controller initializer, or load it before to initialize it and pass it? Or do you add the code to viewDidLoad?
Usually this done inisde viewDidLoad e.x in MVC you call the controller to load the data and refresh the contents of the vc like table/collection , regarding sending data case this known as dependency injection
Also, what about the data for the rootViewController?
rootViewController is same as any other vc
First of all let me suggest not to have data loading code in ViewController if your app is not very very simple. It would be a better practice to have it in a separate class. Check out MVP or VIPER design patterns for more info.
As for when to load your data (after view loaded or before) is more of a UX decision.
If you start loading after, of course, screen might be empty for a while, it would be good to have some loading indicator on the screen so that users do not get confused. Whether users will see empty screens often or not depends on whether you store your loaded data permanently (in a local DB), have some run time cache, or non of that. If you have some caching, empty screen will be visible just once, because if data is loaded and cached you will show that and update the screen after new data is received.
You can also start loading data before view is loaded (in loadView()) or may load some minimal data for the views which are not visible (so that they are not completely empty when opened) and then load full data after view is loaded.
Any approach you choose is highly dependent on what kind of user experience do you want to provide, also on how long it takes to load your data.
Answer in one line is viewididload but below is the actual structural method.
So for this you can have multiple answer the most popular answer for these days would be using MVC/MVVM. So let's discus about MVVM with delegate/protocol
Model - your data.
View model - here you initialize/modify/handle your data.
Viewcontroller - viewdidload - here you can initialize your view model and interact with the data that can be provided by view model with the help of delegate-protocol mechanism.
I'm looking to implement a contacts list into my app. I have the add contact working and it actively uses NSCoding for writing to the disk for the contacts. It will update properly between the list of contacts and the new contact view, but doesn't when I switch from another view.
Do I need to prepareforsegue when coming from the non-contact views in a way that prepares the data? Or do I need to create my Set<Contacts> in the first view and pass references through that when going between views. As of now it only updates data when I do my unwindsegue from the add contact view controller.
I would need more context (code) to be sure this will solve your case.
But I had similar issues in the past for 1 simple reason:
I put the refresh code in ViewDidLoad(). This is called only when the ViewController is loaded (first time it appears), not every time you switch back to it (doesn't reload it).
You should put your code in ViewDidAppear(), or ViewWillAppear() if you want it executed each time you "switch" back to your view.
Here is your code:
ViewDidAppear(){
some code
some code
code that refreshes view with contacts
}
Here is what it should be:
ViewDidLoad() {
some code
some code
}
ViewDidAppear() {
code that refreshes view with contacts
}
Code that you want executed each time your view appears on the screen should be put in "ViewDidAppear()".
I am having a basic question for like best practise.
The setup:
ListViewController:
UiTableView with ManagedObjects. The objects will be loaded from a server.
First load 20 objects. Scroll to the end of the table, the next 20 objects will be loaded.
Selecting a cell will load the DetailViewController.
I have a ListObject with an array of the items (and other properties with informations about the list, not relevant for the DetailVC)
DetailViewController:
The details of the selected object are shown. The VC also has 2 buttons to show the next object details or the previous object.
Now e.g. there are 20 objects loaded in the ListVC and I select row 10, the
DetailVC with Object at index 10 will show.
Then I click trough the next objects until object number 20.
Now when i click the 'next' Button, the next 20 objects have to be loaded from the server.
(Show object #21 but there are only 20 objects loaded).
Is there a best practice to load the next objects?
I have a class for data loading.
- (void)loadDataWithRequest:completionHandler:
When the NSUrlSession DownloadTask finishes, the completionHandler gets called and this calls the class ApiParser for json parsing and adds the result to the List Object.
So i have to call this in the DetailVC for the next 20 objects.
Now what is the best practice to do this? Or is there a better way to implement the data loading?
I could pass a ListVC reference to the DetailVC and call [listvc loadDataWithRequest:completionhandler] and load the detail from [listvc.listobject.items objectAtIndex]
Or i just could pass the ListObjectItems to the detailVC. Somehow load the new objects and
have a KVO to the ListObjectItems count.
Other methods would be delegates or NotificationCenter.
But i guess the best practice is not to put the loadData Method in the ListVC, but somewhere else.
How about to put the dataLoad method as instance method in the ListObject class and listen for the KVO in ListVC and DetailVC?
So many possibilities. But what is a good way?
When the "next" or "previous" buttons are pressed you could inform the "master table view" of the action. This would then load the next batch if necessary before presenting the detail view. Don't block the UI though, show a spinner or something in the blank detail view while waiting for the next batch to come back.
In summary: don't put any loading logic in your detail view. It should only be responsible for displaying the "details" and possibly a loading spinner if you tell it to display nil (as in, not finished loading yet).
Ideally, you would handle the data to be diplayed, in this case the ListObject, seperate from the view controller which is displaying the data, in this case the ListVC or DetailVC.
Each VC should then keep a reference to the data source, which could be a class holding the ListObject, called ListDataSource. This class should hold the methods for loading data from the server. Then, each VC can tell the ListDataSource to load 20 more objects.
This delegation of responsibilities is a pretty good example of a common programming paradigm called MVC. More on that here.
Sorry for the late response and thanks for all the replies.
I now have a ListObject method to load the next data.
And in the ListVC and the DetailVC I use KVO to listen to changes to the ListObject.
Works good so far...
I have an app that has a containerView that changes its view based on the clicking of one of three different tabs. Each tab contains different pieces of contract data.
It's now come the time for me to get ALL of the data from those tabs but I'm not sure the best method. Delegation is 1:1 and therefore I don't think would work as I can't be sure that each tab has been loaded. Same goes for the NotificationCenter as each has to register as an observer.
I've considered iterating through each and passing the message "view", this will verify each has been loaded, then firing off a Notification or while inside of each view calling a method to get me the data I need so that I'll end up with one large dictionary of values.
Any other ideas or commentary on my possible solution?
Let me know if more details are needed, this was a poor design from the start but I was required to implement it like this as the clients had approved the design BEFORE I started at this company and it took them several weeks to approve anything.
I've solved the issue of every subchild not being visible by calling [subviewName view] to assure that viewDidLoad was fired. Inside viewDidLoad I register for the notification, now I've assured that each view can create a dictionary and pass back it's information to the parent.
Umm I really need some help
I have a search view, when user clicks the cell (basically a game), the view changes to a Game View. Atm I was not using any async stuff so while the game view loads, the app kinda hangs for 2-3 secs on the search view after clicking the cell (since the viewDidLoad method of game view loads all data from web API)
What I wanted to do was when a user clicks game in searchView, the gameView instantly shows up with just a blank view and a Activity Indicator meanwhile the data is fetched from API.
Once data is fetched, the gameView (which is a tableView btw) reloads the data and shows it accordingly from the fetched data.
If it makes any diff. I am using static cells for gameView and thus not using cellAtRowIndex code.
I have attached images for some of my methods I tried to use as per my searching on SO for AFNetworking, async etc etc.
The table IB elements are filled from data in a separate function showGame, I am calling this function in rowsForSection since table will be reloaded after block completion. Excuse me but I wasnt sure what would be the best way to reload table or rather show the data.
Atm my app is showing me my default static content which I created in interface builder (and with only 1 row). AKA the table is not being updated. I used breakpoints to check and yeah the showGame is getting correct data, passing correct data to IB elements. Even the execution is reaching the point in code to return 5/6 rows BUT the actual app/view is not updating properly.
For those who want pastebin text code, here it is:
.m file where async code to fetch game from API is
http://pastebin.com/p2FETbku
.m file of my gameView class/tableView which shows the table view and data, this file also has the showGame method to show the game data fetched from above class
http://pastebin.com/cWtSKPw0
Please excuse my code or any small mistakes, I am still in learning phase and just started 3 weeks ago.
The static cells were working fine before I was using async method, somehow its getting messed up during/after that reloaddata
One point that I think you are missing is that execution of this statement
GDgameViewController *gdv = [[GDgameViewController alloc] init];
doesn't return the tableViewController hence the tableView that is currently presented.
So in your function +(void) fetchGame:(NSString *)gid what you are reloading is the tableView that is not presented.
To help you further I have created a project for you which use a bit different approach to do your work but it does it optimally. You can find project here. If you don't know how to use gitHub, don't worry just download the zip file. Please make this count as