I have a tableview that is populated using a Parse query. However, all of the cells are loaded with the first object from the query results array. For example, if the query returns [User1,User2] the two cells in the tableview will show "User1". What am I doing wrong?
Here's a gist of the file:
https://gist.github.com/jtansley/2329c6fa4baa63f48ee2
Hopefully the issue is obvious to experienced programmers.
Thanks!
After reading your code, I think you don't know that findObjectsInBackgroundWithBlock is a function run in another thread but main thread.
Therefore, in func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell , your change in UI would be wrong.
One more things, to improve your app performance, you shouldn't query in cellForRowAtIndexPath! Do your queries in viewDidLoad instead. Fill your database on Parse to an array, then load it inside cellForRowAtIndexPath function.
I couldn't code for you since I'm hanging out with my gf. ;)
Hope this help.
Related
I am wondering how did you manage to correctly respect MVC design pattern on your IOS developments with Swift ? What I feel right now is that a view controller mix both the controller part and the view part but it feels wrong right ? To respect MVC, we clearly need to separate the controller and the view. How did you make this ?
Maybe, it's more obvious with other design patterns like MVVM or MVP ?
You can separate your project and create a structure where you have all the logic and models in one place and all the viewControllers in one place.
Like this for example:
Core
Models
Person.swift
Car.swift
Helpers
Extensions.swift
APIHelper.swift
Webservice
Webservice.swift
Controllers
ViewController.swift
SecondViewController.swift
So you basically have all the logics and calculations in your Core and all the views and UI elements in your Controllers. With this way you won´t have to do the same logic code multiple times. You could also create custom views and add them to your Core which you later can call in your Controllers.
Although it might be too broad to answer such a question, I will answer your specific issue about:
view controller mix both the controller part and the view part...
Note that when working an iOS project, it leads you implicitly to apply the MVC pattern. By default, the view controllers represent the Controller part, the .storyboard and .xib files represent the View part and any model objects for encapsulating the data (data templates) represents the Model.
What I feel right now is that a view controller mix both the
controller part and the view part but it feels wrong right ?
The view controller has many responsibilities to be handled, in addition to interprets user actions, it should also have to be the intermediary between view and the model(s), and don't forget about handling the integration with web services... That's the issue of the Massive View Controller.
if you tried to do some researches about solving this issue, you would find many approaches to follow such as applying other structural patterns like MVVM, MVP , VIPER or Clean Architecture or even simpler approaches such as dividing your project files to increase the jobs independency which leads to make it more clear and easy to trace, MVC-N might be a good example.
But for the specific case that are you asking about (mix both the controller part and the view part) to keep it simple: I would recommend to separate the logic of the data representation based on its view, example:
One of the most popular case when building an iOS project is working with table view and its cells, consider the following:
ViewController.swift:
class ViewController: UIViewController {
// ...
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myDataSourceArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
// ...
return cell
}
}
MyCustomCell.swift:
class MyCustomCell: UITableViewCell {
#IBOutlet weak var lblMessage: UILabel!
}
Now, imagine that -somehow- there is a requirement to change the lblMessage label text color in the cell based on a bunch of complex calculations, for such a case:
Do not do this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
// do calulations which might needs tens of lines to be achieved, based on that:
cell.lblMessage.textColor = UIColor.red
return cell
}
That leads to make the view controller to be massive and contains alot of jobs, instead do:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCellID") as! MyCustomCell
//...
cell.doCalaulations()
return cell
}
In the cell class:
class MyCustomCell: UITableViewCell {
#IBOutlet weak var lblMessage: UILabel!
func doCalaulations() {
// do calulations which might needs tens of lines to be achieved, based on that:
blMessage.textColor = UIColor.red
}
}
That leads to make the project components to be more encapsulated and the important thing is the view controller does not has to take care of the whole thing. For me, in cases that similar to this one, I would prefer to even make blMessage to be private which guarantees to make it only editable from the owner class (more encapsulation) to handle any needed behavior, thus any view controller should call the class methods instead of direct accessing to its properties and its IBOutlets.
Separate a usual ViewController on two different parts: View and Presenter. View responses only for displaying data and collecting user's interaction. Presenter prepares data for view and process user actions comes from View.
This idea came from Clean Architecture by uncle Bob and realized into VIPER architecture.
View has two protocols:
ViewInput : contains functions for pass data to display like set(labelText: String). This protocol should implement View. Presenter has object of type ViewInput.
ViewOutput : contains functions, which calls when some events happen in view like viewDidLoad() or rowDidSelect(at: IndexPath). This protocol should implement Presenter. View has object of type ViewOutput.
VIPER isn't trivial thing, I spent several days to understand its principles. So read articles and try to implement it in your code. Do not hesitate to ask questions.
The documentation for the NSTableViewDataSource protocol says
This method is mandatory
and if you create a new NSTableViewDataSource the compiler asks you to provide this method as well as numberOfRows(in tableView: NSTableView).
So far, so good. And if you provide it, it promptly gets called once for every row... but if you delete it, the table seems to work every bit as well as it does without. There seems to be absolutely no connections in a view-based table to this method: in the NSTableViewDelegate's tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) method, I return new views based on the content array of my datasource, and if I stick a bunch of nonsense data into objectValueFor: row (up to and including returning the same string - my table objects are not strings) is has no effect whatsoever because I'm setting the values of my NSTableViewCells in viewFor tableColumn:.
If I'm handling selection changes (and anything I want to do with objects) by retrieving the corresponding value from the datasource directly (e.g.)
func tableViewSelectionDidChange(_ notification: Notification) {
print(dataSource.allItems[tableView.selectedRow])
}
is there any reason to implement this method? I cannot see any point where that object is actually used, and it feels like an artefact from cell-based tables.
I don't want to break my code by leaving out a necessary method, but since I'm about to try something weird and wonderful with a custom datasource, I also don't want to overcomplicate my code with a method that gets called, but the result of which seems completely meaningless.
If the cell view responds to setObjectValue:, then the table view calls that method and passes in the object value for the row that was obtained from tableView(_:objectValueFor:row:).
NSTableCellView does respond to that method and is a common cell view class. NSTextField does, too; it actually inherits it from NSControl.
In your case, your cell view either doesn't have an objectValue property or, more likely, you're just not using it.
One common configuration is to use NSTableCellView as the cell view and then use bindings to bind the subviews to key paths going through its objectValue property.
i am looking for the best solution create a TableView with different Layouts.
For Example: I need a few rows, with with a Label and a Textview, and some with a Label and a Textbox.
At the Moment i have 2 Custom Cells, and create an instance of them in my TableView function, and return the correct cell depending on my indexPath.row (this is also the key in my Array where i cant find out with type of Layout i need).
Is this the correct way do handle this?
Another Question is how to get these Values from the Textbox. Should i use the "AfterEdid" Action from TableView/Textbox and Write them into one global Array? Or is it possible to retrieve the Values easer from a Textbox/Testview inside a cell in a Table?
Thanks in Advance
The correct way is to define two cell prototypes in the Storyboard, each one with a different cell identifier. The in the UITableViewDataSource, method tableView: cellForRowAtIndexPath, you should dequeue the cell of correct type based on your application logic.
I've seen various similar posts to this but none seem to match my situtation/solve the problem.
I have a tableview whose delegate and datasource are set to the VC it sits within.
On load this happily calls all the usual methods:
numberOfSectionsInTableView
numberOfRowsInSection
cellForRowAtIndexPath
however at this point the array containing my data is empty.
Once it is populated (after location lookup) [tableView reloadData] is called. This successfully fires:
numberOfSectionsInTableView
but neither of the following are recalled
numberOfRowsInSection
cellForRowAtIndexPath
I have even tried returning different results (0/1) in the numberOfRowsInSection method to try and force it with no result
Thanks for any help
I found the error.
The client had been playing with the code and changed the viewForHeaderInSection so that in some cases it was returning nothing (not even nil).
This must have prevented the other methods from firing, despite their being no exception or console log warning.
I have a UITableView with style "Grouped" which I use to set some options in my App. I'd like for one of the cells of this UITableView to only show up depending on whether another of this UITableView's cells is activated or not. If it's not, the first cell should show up (preferably with a smooth animation), if it is, the first cell should hide.
I tried returning nil in the appropriate -tableView:cellForRowAtIndexPath: to hide the cell, but that doesn't work and instead throws an exception.
I'm currently stuck and out of ideas how to solve this, so I hope some of you can point me in the right direction.
You should remove the data behind the hidden cells from the table view's data source.
For example, if you are using an array, when an action occurs that causes a cell to be hidden, you would remove the object for that row from the array. Then, as the table view's data source, the array will return one less total count and only return valid cells for every row in that count (no nil).
This approach may require maintaining a second array with all of the objects (including hidden).
To update the view, check out reloadRowsAtIndexPaths:withRowAnimation:.
Here's a handy post in which the author provides some source code for performing animations on the currently selected cell:
http://iphonedevelopment.blogspot.com/2010/01/navigation-based-core-data-application.html
He's using this in a NSFetchedResultsController context, but you can see how he's using various calls to add/remove cells & sections.
Now, in your case, you'll need to modify whatever array you're using to host the data used to generate the rows in your tableView when you "activate" your cell, then selectively use:
tableView:insertRowsAtIndexPaths:withRowAnimation:
tableView:deleteRowsAtIndexPaths:withRowAnimation:
tableView:insertSections:withRowAnimation:
tableView:deleteSections:withRowAnimation:
to adjust things accordingly (you can start with tableView:reloadData:, but it's inefficient).
I realize that the API can be a bit daunting, but take the time to read through it and understand what the various calls do. Understanding how the UITableView uses its datasource and delegate, as well as the chain of events that occur when cells are selected/deleted/etc., is important if you want to get things just right (and crash-free).
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:withRowAnimation:]; // or insertRowsAtIndexPaths:withAnimation:
[tableView endUpdates];
Before cellForRowAtIndexPath is called, numberOfRowsInSection is called. You should return the appropriate value of cells in the section there, so if you only want to show 1 cell, return one. The logic what cells are shown has to be implemented partially in both methods