How to Insert a Row in Tableview Without Using Reloaddata Function in Cocoa

Updating A Specific Row In UITableView Without Reloading The Whole Table

Using this method - (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation is correct. However, you should keep track of the value to be loaded in every row in each cell, and then access them every time a row is reloaded when the user scrolls the table.

Here is a sample:

@interface ViewController ()<UITableViewDelegate, UITableViewDataSource>

@property NSMutableArray *labelValueListForSection0;
@property NSMutableArray *labelValueListForSection1;
@property NSMutableArray *labelValueListForSection2;

@end

@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];

_labelValueListForSection2 = [[NSMutableArray alloc] initWithObjects:@"value1", @"value2", @"value3", nil];
}

- (void)changeAnItem
{
[_labelValueListForSection2 setObject:@"ChangedValue" atIndexedSubscript:1];

[_table reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:1 inSection:2]] withRowAnimation:UITableViewRowAnimationNone];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"SimpleTableItem";

UITableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:identifier];

if(tableCell == nil)
{
tableCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}

switch(indexPath.section)
{
case 0:

tableCell.textLabel.text = @"Test";

break;

case 1:

tableCell.textLabel.text = @"Test";

break;

case 2:

tableCell.textLabel.text = [_labelValueListForSection2 objectAtIndex:indexPath.row];

break;

case 3:

tableCell.textLabel.text = @"Test";

break;

case 4:

tableCell.textLabel.text = @"Test";

break;

default:
break;
}

return tableCell;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 5;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}

@end

As you can see, I am keeping track of the value of each row and I am storing them inside an array. In my case, I have an array of values for each row for a specific section. So that when this method

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; 

is called, it will fetch the value that should be assigned to that row.

In case you don't know, iOS is reusing each table cell for better performance and better memory management. So, if you encounter cases such as why is a value of one of my rows is duplicated in other rows, it is because that instance of tableCell is reused in other rows.

So, in order to make sure that each time a cell is loaded, and the value should be correct. You have to keep track of the value for each row and reassign it to that cell each time it is reloaded.

Hope this will help you solve your problem.

how to keep the visible content after nstableview reloaddata?

I got this problem fixed: first, I got the rect size changed amount; then after the reloadData: , scroll the tableview to the changed point (original point + delta parts) immediately with the clipview's scrollToPoint: method. It does it so fast that you cannot realize the operation there.

How does [self.tableView reloadData] know what data to reload?

Okay, I understand your frustrations because the vast majority of iPhone instructional material do not pay sufficient attention to overall app design. They make a beeline for the eye candy interface and pay only lip service to way that the app should handle the data even though handling data is the entire purpose of the app in the first place!

The instructional materials do not spend enough time explaining the Model-View-Controller design pattern on which the entire iPhone/Cocoa API is based. You're having a hard time understanding anything because you keep trying to cram functionality into the wrong objects under the mistaken belief that the UI view is the core of the program as the instructional materials have led you to believe. Under this misapprehension, nothing makes sense, not even the Apple Documentation.

You need to step back and rethink. It is not the function of a view to decide what data to display and when to display it. It is not the function of the table view controller to hold, manage or store the app's data. Those functions properly belong to the data model object (which you've possibly never heard of.) You're having trouble because you are trying to split the data model task across the view and the view controller were they do not belong.

Apparently, your app doesn't even have a data model because you are holding the table's data as properties of the tableview controller. Although you often see this in simplistic tutorial examples, it is bad design which will collapse under the complexity of any but the most trivial apps.

Instead, your data should be stored in and managed in its own custom object. This is the data model. In your case, it sounds like you have data spread across two arrays so you would create a data model object something like this:

@interface MyDataModel : NSObject {
@protected
NSArray *arrayOne;
NSArray *arrayTwo;
@public
NSArray *currentlyUsedArray;

}
@property(nonatomic, retain) NSArray *currentlyUsedArray;

-(void) switchToArrayOne;
-(void) switchToArrayTwo;
-(void) toggleUsedArray;

@end

#import "MyDataModel.h"

@interface MyDataModel ()
@property(nonatomic, retain) NSArray *arrayOne;
@property(nonatomic, retain) NSArray *arrayTwo;

@end

@implementation MyDataModel

- (id) init{
if (self=[super init]) {
self.arrayOne=//... initialize array from some source
self.arrayTwo=//... initialize array from some source
self.currentlyUsedArray=self.arrayOne; //whatever default you want
}
return self;
}

-(void) switchToArrayOne{
self.currentlyUsedArray=self.arrayOne;
}

-(void) switchToArrayTwo{
self.currentlyUsedArray=self.arrayTwo;
}

- (void) toggleUsedArray{
if (self.currentlyUsedArray==self.arrayOne) {
self.currentlyUsedArray=self.arrayTwo;
}else {
self.currentlyUsedArray=self.arrayOne;
}
}

(Notice that the actual data is encapsulated and that other objects can only access the currentlyUsedArray. The data model decides which data to provide based on the internal state of the data.)

This data model object should be in a universally accessible location. The best method is to make it a singleton but the quick and dirty method is to park it as an attribute of the app delegate.

So in you tableview controller you would have a property:

MyDataModel *theDataModel;
@property (nonatomic, retain) MyDataModel *theDataModel;

then in the implementation

@synthesize theDataModel;

-(MyDataModel *) theDataModel; {
if (theDataModel; !=nil) {
return theDataModel; ;
}
id appDelegate=[[UIApplication sharedApplication] delegate];
self.theDataModel=appDelegate.theDataModelProperty;
return theDataModel;
}

Then in your tableview datasource method:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
...
cell.textLabel.text=[self.theDataModel.currentlyUsedArray objectAtIndex:indexPath.row];
return cell;
}

If some event anywhere in the app requires you to switch arrays, you just call up the data model object from the app delegate and send it the appropriate switch array message.

id appDelegate=[[UIApplication sharedApplication] delegate];
[appDelegate.theDataModelProperty toggleUsedArray];

Now all subsequent data operations, whether in that particular table view or some other completely unrelated view, will use the data form the proper array.

Why go through all this trouble? It makes the application modular. You can easily add on different views each of which display the data in a different manner without having to rewrite your data management every single time. You can use the data model to manage data that will be displayed in a table, in a webview or on the command line. You can even easily move the data model to an entirely different app.

This modularity makes the management of large complex apps so much easier. You have only one object that manipulates and controls the data. You don't have to worry that some minor error in some rarely used code segment will trash the entire app. You can plugin views easily or remove them easily without breaking the app.

This is of course a trivial example but it shows good practice.

However, you may ask, how does this solve the problem of the tableview knowing what data to load and when to load it? Simple, it doesn't. It is not the job of the tableview to know what data to load or when to load. The data model handles the what-data and tableview controller handles the when. (You can even have the data model issue notifications when it is updated e.g. for a url. then the view controller can register for the notification and call reloadData whenever the data model changes.)

By ruthlessly compartmentalizing and encapsulating functionality in MVC, you create complex apps from simple, reusable components that are easy to maintain and debug.

It's really to bad most instructional materials only pay lip service to this utterly critical concept.

reloadData in NSTableView but keep current selection

It depends on how you populate your NSTableView.

If you have the table view bound to an NSArrayController, which in turn contain the items that your table view is displaying, then the NSArrayController has an option to preserve the selection. You can select it (or not) from within Interface Builder as a property on the NSArrayController. Or you can use the setPreservesSelection method in code.

However, if you completely replace the array of items that the NSArrayController manages each time you get your FSEvents, then maybe the preservation of selection cannot work. Unfortunately the Apple docs on this property of NSArrayController are a bit vague as to when it can and cannot preserve the selection.

If you are not using an NSArrayController, but maybe using a dataSource to populate the table view, then I think you'll have to manage the selection yourself.

why reloadData shouldn't be called on methods that delete/insert rows

Since - [UITableView reloadData] reloads all the data, it basically invalidates any temporary, non-permanent changes to the UI. You should either reflect these changes in your data structure, or, and I think it's generally a better idea, stick to Apple's default methods (try changing the graphics only, not the logic).

Cocoa:NSTableView repeated loading data the label in cell

Table views make use of a cell reuse queue to make them more performant, particularly when they contain lots of rows. This is similar to how UITableView works on iOS if you're familiar with it.

In brief: when you scroll the table view, the views for the rows which move offscreen are not deallocated but instead put into a 'reuse queue' for later. When you call the makeViewWithIdentifier(_:owner:) function, the NSTableView first checks this queue for views that it can reuse: if it contains any, the function will return one of these to you; if not, it will make a new view and return that.

I suspect the problem here is that you're adding subviews to the cell view in tableView(_:viewForTableColumn:row:), which is executed every time a new row is set up. Since the cells you're adding subviews to are reused, every time a new row scrolls into view you add another label and image view to it, which is stacked on top of the previous one and creates the bolding effect you describe.

If you're designing your cells in Interface Builder, you can add a text field and image view to your prototype cell and connect NSTableCellView's imageView and textField outlets to these views so you can access them in tableView(_:viewForTableColumn:row:)

How to reload and animate just one UITableView cell/row?

Use the following UITableView instance method:

- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation

You have to specify an NSArray of NSIndexPaths that you want to reload. If you just want to reload. If you only want to reload one cell, then you can supply an NSArray that only holds one NSIndexPath. For example:

NSIndexPath* rowToReload = [NSIndexPath indexPathForRow:3 inSection:0];
NSArray* rowsToReload = [NSArray arrayWithObjects:rowToReload, nil];
[myUITableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];

You can see the UITableViewRowAnimation enum for all the possible ways of animating the row refresh. If you don't want any animation then you can use the value UITableViewRowAnimationNone, as in the example.

Reloading specific rows has a greater advantage than simply getting the animation effect that you'd like. You also get a huge performance boost because only the cells that you really need to be reloaded are have their data refreshed, repositioned and redrawn. Depending on the complexity of your cells, there can be quite an overhead each time you refresh a cell, so narrowing down the amount of refreshes you make is a necessary optimization that you should use wherever possible.



Related Topics



Leave a reply



Submit