Old rows are not getting deleted datagrid


#1

Hi!. I’m using the shobigrid for a while now and ran into a wierd situation. When i update my shinobigrid and the new data has fewer rows then currently displayed the old rows that are displayed and not overriden remain the same, when these rows should be deleted. I tried calling reload, creating a new grid reddrawRows and reloadRows with a array of all visible rows but non seem to be working. The old rows that remain cannot be clicked so somehow the grid is aware that they arent there anymore but still displays it. Any ever experienced it before? or can help me out fixing it?


#2

Greetings Program!

You should only need to do a reload; verify that the item was removed from the datasource before you do the reload.

I added the following to the GettingStarted app and was able to remove the rows:

- (void)viewDidLoad {
    // Existing code

    shinobiDataGrid.delegate = self;
}

- (void)shinobiDataGrid:(ShinobiDataGrid *)grid didSelectRow:(SDataGridRow *)row {
    [self.data removeObjectAtIndex:row.rowIndex];
    
    [grid reload];
}

Wg


#3

hmm that’s strange. I do the following:

    CGRect rect = CGRectMake(10, 240, 750, 750);
     _shinobiGrid = [selfcreateShinobiGridColumns:_shinobiGridrekt:rect];
     [self.viewaddSubview:_shinobiGrid];
    gridDatasource = [[SearchMachinesGridDataSource alloc]initWithData:machines];
     _shinobiGrid.dataSource = gridDatasource;
     _shinobiGrid.delegate = self;

    [_shinobiGridreload]; I initiate the datasource with the new information. for example first run it has 220 entries, second run 0 (i checked this in the debugger). it still showing the already displayed 12 rows. these rows arent selectable anymore and the grid isnt scrollable anymore so the grid is aware that they shouldnt be there. When i reload the datasource with 12 or more entries(as much as are displayed at once) it works fine and the rows get updated. when i update the datasource to 6 rows 6 rows get updated and 6 old rows remain. So the datasource is getting updated correctly but it looks like the grid wont do a complete clear before adding the new rows.
 The datasource exist only out of the array with data and a UIView, the UIView is because the needed UIView <SDataGridDataSource> superclass.

#4

Does SearchMachinesGridDataSource implement the SDataGridDataSource protocol?

Required methods:

numberOfRowsInSection
prepareCellForDisplay

Or is it the class that you are assigning the datasource in (self)? If it is then assign dataSource = self.

It is possible that it may be the way you are removing the items from the datasource or if you’re instantiating a new datasource and when reload is called.

I haven’t been able to recreate the issue in the GettingStarted sample app; are you able to share more code of the datagrid implementation methods in each of your classes?

Wg


#5

SearchMachinesGridDataSource implements those to methods. i copied the class here below. The delegate methods are available in self. To remove items from the grid i assign a complete new datasource to the grid. The reason i create a new datasource and assign it to the grid is because the grid shows the results from a search function, if for example the user changes the category all old values need to be deleted and it need to be filled with new ones. Do u know any alternatives to accomplisch this? looping over the array to delete the rows one by one and after that loop again to fill the rows seems like a bad practice to me. On other parts of my code where its possible i implemented the delete function the normal way and it works perfect.

@implementation SearchMachinesGridDataSource
 
-(id)initWithData:(NSArray*)data{
    _machines = data;
     returnself;
}
 
-(NSUInteger)shinobiDataGrid:(ShinobiDataGrid *)grid numberOfRowsInSection:(int)sectionIndex
{
     return_machines.count;
}
 
- (NSUInteger)supportedInterfaceOrientations
{
     returnUIInterfaceOrientationMaskPortrait;
}
- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell *)cell
{
     // both columns use a SDataGridTextCell, so we are safe to perform this cast
     SDataGridTextCell* textCell = (SDataGridTextCell*)cell;
    
     // locate the person that is rendered on this row
    Machine* machine = _machines[cell.coordinate.row.rowIndex];
     // determine which column this cell belongs to
     if ([cell.coordinate.column.titleisEqualToString:@"Type"])
    {
         // render the name in the 'name' column
        textCell.textField.text = machine.type;
    }
     elseif ([cell.coordinate.column.titleisEqualToString:@"Bouwjaar"])
    {
        textCell.textField.text = machine.bouwjaar;
    }
     elseif ([cell.coordinate.column.titleisEqualToString:@"Technische staat"])
    {
        if(machine.technischeStaat != [NSNull null]){
        textCell.textField.text = machine.technischeStaat;
        }
    }
     elseif ([cell.coordinate.column.titleisEqualToString:@"Fabrikaat"])
    {
        textCell.textField.text = machine.fabrikaat;
    }
}
 

@end

#6

Here’s what you should do.

  1. Keep a reference to the primary data (all records) in self.
  2. Send the filtered data (grid data) to SearchMachinesGridDataSource on init.
  3. When deleting more than one record (or one), loop through the objects and delete the object(s) from both the primary data and filtered data (or delete from the primary data only and then filter it again).
  4. Always filter the primary data and assign the result to the machines property in the same SearchMachinesGridDataSource instance.

Here’s modifications to the GettingStarted sample app:

#import "ViewController.h"
#import "PersonDataObject.h"
#import "PersonDataSource.h"
#import "SearchMachinesGridDataSource.h"

@interface ViewController ()

@property NSMutableArray *machines;
@property NSMutableArray *filteredData;
@property ShinobiDataGrid *shinobiDataGrid;
@property SearchMachinesGridDataSource* gridDatasource;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [ShinobiDataGrids setLicenseKey:@"your license key"]; // TODO: add your trial license key here!
    
    // Create a grid - with a 40 pixel padding
    _shinobiDataGrid = [[ShinobiDataGrid alloc] initWithFrame:CGRectInset(self.view.bounds, 40,40)];
    
    // Add a name column
    SDataGridColumn* nameColumn = [[SDataGridColumn alloc] initWithTitle:@"Name"];
    nameColumn.width = @484;
    [_shinobiDataGrid addColumn:nameColumn];
    
    // Add an age column
    SDataGridColumn* ageColumn = [[SDataGridColumn alloc] initWithTitle:@"Age"];
    ageColumn.width = @200;
    [_shinobiDataGrid addColumn:ageColumn];
    
    // Create some data to populate the grid
    _machines = [[PersonDataSource generatePeople:20] mutableCopy];

    // Filtered data to datasource.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"forename beginswith[c] 'a'"];
    _filteredData = [[_machines filteredArrayUsingPredicate:predicate] mutableCopy];
    
    _gridDatasource = [[SearchMachinesGridDataSource alloc] initWithData:_filteredData];
    
    // Set the data-source, this is the class responsible for supplying data to the grid.
    _shinobiDataGrid.dataSource = _gridDatasource;
    
    [self.view addSubview:_shinobiDataGrid];
    
    _shinobiDataGrid.delegate = self;
}

- (void)deleteMachine:(PersonDataObject *)machine {
    // Remove the object from the primary and filtered data.
    [_machines removeObject:machine];
    [_filteredData removeObject:machine];
}

#pragma mark - ShinobiDataGridDataSource methods

// Just for testing I'm doing it on row selection.
// Delete record or re-filter.
- (void)shinobiDataGrid:(ShinobiDataGrid *)grid didSelectRow:(SDataGridRow *)row {
    // Get the machine object.
    PersonDataObject* machine = [_gridDatasource.machines objectAtIndex:row.rowIndex];
    
    // Remove it from the primary and filtered data.
    [self deleteMachine:machine];

    // Finally, reload the grid with the updated data.
    [grid reload];

// Or you can do this:

    // Filtered data to datasource.
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"forename beginswith[c] 'c'"];
    _filteredData = [[_machines filteredArrayUsingPredicate:predicate] mutableCopy];
    
    // Set the filtered data again without instantiating another SearchMachinesGridDataSource.
    _gridDatasource.machines = _filteredData;

    // Finally, reload the grid with the updated data.
    [grid reload];
}


@end



@interface SearchMachinesGridDataSource : UIView <SDataGridDataSource>

@property (strong) NSMutableArray *machines;

- (id)initWithData:(NSMutableArray *)data;

@end

The only thing different from your code here is the call to super in initWithData which you should also implement.

#import "SearchMachinesGridDataSource.h"
#import "PersonDataObject.h"

@implementation SearchMachinesGridDataSource

- (id)initWithData:(NSMutableArray *)data {
    self = [super init];
    if (!self) return nil;
    
    _machines = data;
    
    return self;
}

#pragma mark - ShinobiDataGridDataSource methods

- (NSUInteger)shinobiDataGrid:(ShinobiDataGrid *)grid numberOfRowsInSection:(NSInteger)sectionIndex {
    return _machines.count;
}

- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell *)cell {
    // Both columns use a SDataGridTextCell, so we are safe to perform this cast
    SDataGridTextCell* textCell = (SDataGridTextCell*)cell;
    
    // Locate the perspon that is rendered on this row
    PersonDataObject* person = _machines[cell.coordinate.row.rowIndex];
    
    // Determine which column this cell belongs to
    if ([cell.coordinate.column.title isEqualToString:@"Name"]) {
        textCell.textField.text = person.forename;
    }
    else {
        textCell.textField.text = [person.age stringValue];
    }
}

@end

Wg


#7

I Got it to work. Thanks allot :). The problem was indeed in the fact that i assigned a new datasource to the grid. When i update the datasource instead of creating a new one it works lika a charm.