-->

NSTableView + NSSegmentedControl + NSSegmentSwitch

2019-08-16 01:17发布

问题:

I've got a table view which has an NSSegmentedCell for its right-most column.

The NSSegmentedControls which the cells are pulled from are using the "any" tracking mode (aka: multiple selection) and the segments are selected based on a bitfield with possible values: 1, 2, and 4. A value of 1 means A|b|c. 2 is a|B|c. 4 is a|b|C. 3 is A|B|c, etc...

That bitfield is returned by the data sources's tableView:objectValueForTableColumn:row: method and the cell is returned in the delegate's tableView:dataCellForTableColumn:row: method.

Setting the cells individually in tableView:dataCellForTableColumn:row: doesn't do anything, because the table view calls tableView:objectValueForTableColumn:row: just after that to get the value for that column and then uses it to set the "value" of the segmented control/cell.

The problem is at that point.

It looks like the bitfield is being misinterpreted as the index value of the segment: 0 for A, 1 for B, and 2 for C (and -1 for no selection).... which assumes single selection.

How do I get this set up so that that the table view will properly set multiple segments? (eg: bitfield 3 = A|B|c)

回答1:

I did it this way, and it works:
1) make sure you have a reference to segmented control cell in your tableview's datasource/delegate methods; in my project, I just connected segmented control cell with outlet property in my view controller class

2) return anything from table's datasource getter method for the column with segmented control (in my project, repeatDays is an identifier of this column), for example, nil

 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex {
    NSString *colId = [tableColumn identifier];

    if ([colId isEqualToString:@"repeatDays"]) {
        return nil;
    }

    //handle other columns
}

3) implement tableview's delegate willDisplayCell method

- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    NSString *colId = [tableColumn identifier];

    if ([colId isEqualToString:@"repeatDays"]) {
        for (int i = 0; i < self.segmentedCell.segmentCount; ++i) {
            BOOL selected = [[self.days objectForKey:[NSNumber numberWithInt:i]] boolValue];
            [self.segmentedCell setSelected:selected forSegment:i];
        }
    }
}

you need to configure segmented cell when table view is about to display it, do it according to your model details, for example, in my project I have a mutable dictionary days, and it contains bool values corresponding to segments, like {0: NO, 1: YES, 2: NO, 3:YES, 4:NO, 5:NO, 6:NO}, where 0, 1, 2, 3, 4, 5, 6 - segments, and YES/NO - whether this segment selected or no

4) the last, implement tableview's datasource setter method; the object passed to it is clicked segment index; modify your model appropriately, that is, flip flag for clicked segment index:

- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    NSString *colId = [tableColumn identifier];

    if ([colId isEqualToString:@"repeatDays"]) {
        int i = [object intValue];
        BOOL selected = [[self.days objectForKey:[NSNumber numberWithInt:i]] boolValue];
        [self.days setObject:[NSNumber numberWithBool:!selected] forKey:[NSNumber numberWithInt:i]];
    }
}