Can I lazily fetch subsets of the NSTableView datasource?
So that I don't need to load my entire database in memory? So that when the table scrolls, the app is accessing to core data again.
At the moment I've only figured out how to enable/disable lazy fetching for the entire datasource. But I would like to know if there is a way to split the fetching in different runs".
My favorite approach in this case is following.
I keep array of loaded records in my controller. At viewDidLoad this array contains zero elements. Then i request first portions of records from database (restricted by fetch limit of 50 records for example). After records fetched I put them into this array. Also a store a flag indicating is all records loaded or not.
In my table view i always display all records from my array. If not all records loaded then I add one cell with activity indicator. When user scrolls table view down to the cell with activity indicator I request loading of next portion of records from database (using offset and fetch limit again). After records loaded I add them to my array, update flag and call reloadData on my tableView.
Two main methods of my tableViewDataSource looks like:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.allDataLoaded?self.dataArray.count?self.dataArray.count+1;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row>=self.dataArray.count) {
// indicator cell
[self requestReadNext];
UITableViewCell* cell=[tableView dequeueReusableCellWithIdentifier:#"IndicatorCell" forIndexPath:indexPath];
return cell;
} else {
MyCell* cell=[tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
MyObject* obj=[self.dataArray objectAtIndex:indexPath.row];
[self fillCell:cell withObject:obj];
return cell;
}
}
With a core data fetch you can specify an Offset. An offset coupled with a fetch limit gives you a range. When your tableview scrolls to the last object, do another fetch and specify your current count as the offset, and your preferred limit.
Related
I am creating an app in which I have to Reorder the Rows but I have some disabled rows which should not Move. e.g. As shown in image the 2nd and 3rd row are disabled form moving. if I try to move non disabled row they should not move to the index path of the disabled rowCheck this image
Typically you would use this method:
- (NSIndexPath *)tableView:(UITableView *)tableView
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if (proposedDestinationIndexPath.row == DoNotMoveToThisRow) {
return sourceIndexPath;
}
return proposedDestinationIndexPath;
}
If you don't want a cell to move to a certain row, then just return the sourceIndexPath.
As we know, UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];method is invalid after the cell is out of our sight. (it's been put to the pool of reusable cells by system) So how do we track the information inside this cell?
I created another cell variable to point to it.In method -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPathI make _trackMarkCell = cell; trackMarkCell is a property of current controller. This way I can always get the information of the cell. But I feel this way is not that smart, is there any better solution?
Thanks.
So, basically , UITableviewCell is just a view, that is used to display the content in UITableView so from the method -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath you can crate a new cell or if the cell is already in the pool you can reuse it
come to your question, with the information i mentioned above, the cell holds the information that is used to display on tableview so u just no need to worry about the cell, but the data what you are displayed in the cell. u just keep track of the data and while updating the cell, clear all the information of the cell (which is crated newly or reused from the pool) before using it. for example
lets say suppose u are displaying the image in the cell, but after some time, image in the cell which is changed, in this case u just change the image which is present the datasource not in the cell, if u are using the images array to display the images in the cell,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kMenuTableCellReuseId];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kMenuTableCellReuseId];
}
//hear clear all the info present in the cell
//no need of this also u can directly update the cell, but for clarity i mentioned this
cell.myImageView.image = nil;
//just load the cell with new data
cell.myImageView.image = [dataSource objectAtIndex:indexPath.row];
//.... other code
return cell;
}
one more thing using the another cell to keep track of the cell which is present in the pool which might give u different information of other cell. or that cell might be deallocate. so just keep track of the data not the table view cell.
Not sure how you're populating your cells, but if you're feeding the cells via an array of model objects, you'd have access to all your data within that array. You could reference the object with the information you're seeking based on the index within the array (eg. - array[3], where '3' is the index of the object desired)
So how do we track the information inside this cell?
You just don't. Cells don't store information. Cells display information from a data source. You store the data in arrays or Core Data or on a server or anywhere else. Then you just display it in the cells.
What you can do is, you can get data from the cell and put in your collection in UITableViewCell's prepareForReuse method: this will be called before the cell is reused.
I'm new at developing with XCode and Objective-C and I hope you can help me.
The problem is, I have an UITableViewController with an UITableView (created with the InterfaceBuilder).
The cells under the section headers are expandable.
Now I want to dynamically create multiple UITableViews under the existing TableView.
The style will be the same like the existing TableView's style.
Could you tell me how it is possible to create these TableViews programmatically?
Thank you very much
Michael
From what you are saying try using a grouped table view. Check out this link for a quick overview, and go to the grouped table view section.
Edit found this example here:
Seems like it is what you are looking for. And a very cool idea also.
You'll have to just make your own custom header row and just put that as the first row of each section. Subclassing the UITableView or the headers that are on there now would probably be a huge pain and I'm not sure you can easily get actions out of them the way they work now. You could easily set up a cell to LOOK like a header, and setup the tableView:didSelectRowAtIndexPath to expand or collapse the section it is within manually.
If I were you I'd store an array of booleans corresponding the the "expended" value of each of your sections. You could then have the tableView:didSelectRowAtIndexPath on each of your custom header rows toggle this value and then reload that specific section.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
///it's the first row of any section so it would be your custom section header
///put in your code to toggle your boolean value here
mybooleans[indexPath.section] = !mybooleans[indexPath.section];
///reload this section
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
You'd then setup your number numberOfRowsInSection to check the mybooleans value and return either 1 if the section isn't expanded, or 1+ the number of items in the section if it is expanded.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (mybooleans[section]) {
///we want the number of people plus the header cell
return [self numberOfPeopleInGroup:section] + 1;
} else {
///we just want the header cell
return 1;
}
}
You would also have to update your cellForRowAtIndexPath to return a custom header cell for the first row in any section.
I have a UITableView within which I am allowing the user to re-order cells. All cells can be re-ordered except the top row, which must always remain as the top row.
I have used the canMoveRowAtIndexPath method to prevent the top row from being moveable. However, I can't see a way to prevent the user from dragging any of the other rows and dropping them above the top row, thus breaking my top-row requirement.
How do I stop users being able to drag other rows above the top row?
Thanks.
There is a UITableViewDelegate method that allows you to amend the destination index path if it doesn't meet your requirements. I think this should work:
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if (proposedDestinationIndexPath.row == 0) {
return sourceIndexPath;
}
else {
return proposedDestinationPath;
}
}
I'm working on a UI component right now, and as it behaves similarly to UITableView, I'm heavily modeling the delegate and data source protocols after those of UITableView. However, I noticed one method that I don't quite understand- 'canMoveRowAtIndexPath'.
This essentially allows the delegate to specify whether it wants the given cell to be 'movable'. However, wouldn't dropping another movable cell into a higher index than the immovable cell (i.e. 'above' it in the table) cause it to indirectly move anyway? (since every cell below the moved one would be pushed down one row).
So basically, my question is what is the point of this method? Can anyone provide an example use-case for it? Because I'm debating whether I should bother including it in my component or not.
If anything, I would think perhaps a more useful delegate method would be something such as 'canMoveRowInSection', which would allow you to specify whether any rows in a given section can be moved. That would then allow you to disable reordering of a particular section, and moving other rows outside of that section would not affect the ordering of the rows inside it.
I know Apple engineers provided this method for a reason, I just can't see what that reason might be.
Thanks for any insight!
canMoveRowAtIndexPath tells the UITableView that if the table is in editing mode, the cells (or cell, if you choose specifically) can be moved up and down.
It's up to you the developer to handle the other side of that move.
For instance, say you have an array (NSMutableArray to be exact) of "A", "B", "C" and you want to rearrange that array to be "B", "C", "A". You need to make that change in the array based on the location of the cell being moved and save that array.
Example
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
id objectToMove = [[array objectAtIndex:fromIndexPath.row] retain];
[array removeObjectAtIndex:fromIndexPath.row];
[array insertObject:objectToMove atIndex:toIndexPath.row];
[objectToMove release];
}
Section Example
This example says that if the table section is 0, then no cells can move. Any other section (say you have 3), those cells in section 1 and 2 CAN move. You will still need to handle the array accordingly.
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( indexPath.section == 0 )
return NO;
return YES;
}