0
votes

I have an NSTableView that's populated programmatically... For each row I check the value of some variables and fill the cells appropriately. Everything is working well, all the cells are properly filled out and the buttons are all drawn as I want. The catch is that sometimes some rows have an erroneous button drawn that shouldn't be there...

For the column "status" if the value of p.status is "Approved" or "Pending" or "Rejected" the button is drawn and populated. This works.

If the value of p.status is "Completed" no button should be drawn but rather just text should be inserted into the cell. This also works.

But as you can see sometimes there are erroneously drawn buttons where there shouldn't be. I have been working this problem for two days now and can't figure it out. It also appears that there's something being drawn underneath the other buttons as the borders are black not gray.

Do I need to clear out the NSTableView of old drawings first, how would I do that?

enter image description here

Here's the entirety of the code that populates the tableView. (I know, it's long and kind of chunky)

- (NSView *)tableView:(NSTableView *)table_view viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
Proposal *p = [list objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
NSString *holdingValue;
NSString *holdingValue2;
NSString *holdingValue4;
NSString *statusString;

NSTableCellView *cell = [table_view makeViewWithIdentifier:identifier owner:self];

NSString * today = [NSString stringWithFormat:@"%f",[[NSDate date] timeIntervalSince1970]];
NSInteger todayInt = [today intValue];

//Add a notes HUD to the item number cell
if ([identifier isEqualToString:@"itemNumber"]){
    cell.textField.stringValue = [p valueForKey:identifier];
    if(p){
        //Create notes button
        NSButton *myButton = [[NSButton alloc] initWithFrame:NSMakeRect(10, 3, 35, 20)];
        [cell addSubview: myButton];
        [myButton setBordered:NO];
        [myButton setButtonType:NSMomentaryLightButton];
        [myButton setBezelStyle:NSRoundedBezelStyle];
        [myButton setTarget:self];
        [myButton setAction:@selector(notesHUD:)];

        //Change color of notes image
        if(p.notes.length > 1){
            [myButton setImage:[NSImage imageNamed:@"Note2.png"]];
        } else {
            [myButton setImage:[NSImage imageNamed:@"Note1.png"]];
        }
    }

}else if([identifier isEqualToString:@"status"]){
    cell.textField.stringValue = @"";
    if(p){
        //Create popup button
        NSPopUpButton *button = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(2, 1, 200, 22) pullsDown:NO];
        [button setNeedsDisplay:YES];
        [button setBordered:YES];
        [button setButtonType:NSMomentaryLightButton];
        [button setBezelStyle:NSRoundedBezelStyle];
        [button addItemWithTitle:@"Pending"];
        [button addItemWithTitle:@"Approved"];
        [button addItemWithTitle:@"Rejected"];
        [button addItemWithTitle:@"Completed"];
        [button setAction:@selector(selectedAction:)];

        //Check which button we need
        if ([p.status isEqualToString:@"Approved"]){
            NSString *proposalCreationDate = p.dateStatusChanged;
            NSInteger proposalCreationDateInt = [proposalCreationDate intValue];
            NSInteger secondsBetween = (todayInt-proposalCreationDateInt);
            NSInteger numberOfDays = secondsBetween / 86400;
            NSInteger daysSinceCreation = numberOfDays;
            if (numberOfDays == 1){
                statusString = [NSString stringWithFormat:@"%@%ld%@", @"Approved (for ",(long)daysSinceCreation, @" day)"];
            } else {
                statusString = [NSString stringWithFormat:@"%@%ld%@", @"Approved (for ",(long)daysSinceCreation, @" days)"];
            }
            [cell addSubview:button];
            [button removeItemWithTitle:@"Approved"];
            [button setTitle:statusString];
            [button synchronizeTitleAndSelectedItem];
        }else if ([p.status isEqualToString:@"Pending"]){
            NSString *proposalCreationDate = p.dateCreated;
            NSInteger proposalCreationDateInt = [proposalCreationDate intValue];
            NSInteger secondsBetween = (todayInt-proposalCreationDateInt);
            NSInteger numberOfDays = secondsBetween / 86400;
            NSInteger daysSinceCreation = numberOfDays;
            if (numberOfDays == 1){
                statusString = [NSString stringWithFormat:@"%@%ld%@", @"Pending (for ",(long)daysSinceCreation, @" day)"];
            } else {
                statusString = [NSString stringWithFormat:@"%@%ld%@", @"Pending (for ",(long)daysSinceCreation, @" days)"];
            }
            [cell addSubview:button];
            [button removeItemWithTitle:@"Pending"];
            [button setTitle:statusString];
            [button synchronizeTitleAndSelectedItem];
//    
//Here we don't need a button so we just insert text into the cell but sometimes a button gets drawn here.
//
        }else if ([p.status isEqualToString:@"Completed"]){
            NSString *_date = @"";
            if (p.dateCompleted.length > 2){
                NSTimeInterval epoch = [p.dateCompleted doubleValue];
                NSDate * date = [NSDate dateWithTimeIntervalSince1970:epoch];
                NSDateFormatter *_formatter=[[NSDateFormatter alloc]init];
                [_formatter setLocale:[NSLocale currentLocale]];
                [_formatter setDateFormat:@"M/d/y"];
                _date=[_formatter stringFromDate:date];
                statusString = [NSString stringWithFormat:@"%@%@", @"Completed on ",_date];
                cell.textField.stringValue = statusString;
                [button removeFromSuperview];
            } else {
                statusString = @"Completed";
                cell.textField.stringValue = statusString;
                [button removeFromSuperview];
            }
        }else if ([p.status isEqualToString:@"Rejected"]){
            [button selectItemWithTitle:@"Rejected"];
            [cell addSubview:button];
            [button setTitle:@"Rejected"];
            [button synchronizeTitleAndSelectedItem];
        }
    }
}else if ([identifier isEqualToString:@"clientAccessPoint"]){
    holdingValue = [p valueForKey:identifier];
        if (!holdingValue){
            cell.textField.stringValue = @"N/A";
        }else{
            cell.textField.stringValue = [p valueForKey:identifier];
        }
}else if ([identifier isEqualToString:@"uniqueClientID"]){
    holdingValue2 = [p valueForKey:identifier];
        if (!holdingValue2){
            cell.textField.stringValue = @"N/A";
        }else{
            cell.textField.stringValue = [p valueForKey:identifier];
        }
}else if ([identifier isEqualToString:@"dateCreated"]){
    holdingValue4 = [p valueForKey:identifier];
    NSTimeInterval epoch = [holdingValue4 doubleValue];
    NSDate * date = [NSDate dateWithTimeIntervalSince1970:epoch];
    NSDateFormatter *_formatter=[[NSDateFormatter alloc]init];
    [_formatter setLocale:[NSLocale currentLocale]];
    [_formatter setDateFormat:@"M/d/y"];
    NSString *_date=[_formatter stringFromDate:date];
    cell.textField.stringValue = _date;
}else{
cell.textField.stringValue = [p valueForKey:identifier];
}
return cell;
}

----UPDATE----

I've been able to successfully mitigate the overlapping drawing but I'm still getting wonky results when the search is used... here's the updates:

In my header file:

@public
IBOutlet NSPopUpButton *button;
IBOutlet NSButton *notesButton;

In my Implementation file I create the buttons as such:

- (void) awakeFromNib{
//Create popup button
button = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(2, 1, 200, 22) pullsDown:NO];
[button setNeedsDisplay:YES];
[button setBordered:YES];
[button setButtonType:NSMomentaryLightButton];
[button setBezelStyle:NSRoundedBezelStyle];
[button setAction:@selector(selectedAction:)];
}

And then I act on them as such:

[cell addSubview:button];
[button addItemWithTitle:@"Pending"];
[button addItemWithTitle:@"Rejected"];
[button addItemWithTitle:@"Completed"];
[button setTitle:@"Approved"];
[button synchronizeTitleAndSelectedItem];
[button setHidden:NO];
1
Did you mean to post a screenshot?drootang

1 Answers

1
votes

Tableview cells get reused. If you add a button to cells every time they appear the buttons will stack up on top of each other as you scroll.

You can verify this by running your project and then finding the Debug View Hierarchy button (it looks like two perpendicular rectangles on top of one another in the debug bar). Using this you can rotate the view to see the layers. It is super useful for figuring out if a view is not being draw, is hidden by something else, or is offscreen.

To get around this issue only create the button in awakeFromNib and then hide and unhide it and change the text as needed.