0

我在带有数据行的表单上有一个 NSTable,我在表单上有一个按钮,当我单击该按钮时,我调用一个委托/方法来过滤数据并重新加载数据。

使用谓词过滤,返回过滤后的数组,但网格不显示更改的数据

下面显示的是按钮单击的方法

- (IBAction)filterOnClick(id)sender {
NSString *age = @"62";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"Age >= ", age];
NSArray *arr = [arrayPDContent filteredArrayUsingArrayPredicate:predicate];

[gridView reloadData];

}

注意:arrayPDContent 是用于加载数据的 NSMutableArray,上面的 arr 可能不需要。

下面显示的是填充 NSMutableArray 的代码的一部分

NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil];
[arrayPDContent addObject:dictionary];

注意:因为我无法从 MAC OS 内部登录,所以我输入了上面的部分代码

4

3 回答 3

0

我现在正在使用Nate Chandler上面发布的以下代码

- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [[arrayPDContent filteredArrayUsingPredicate:[self filter]] count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[[arrayPDContent filteredArrayUsingPredicate:[self filter]] objectAtIndex:row] objectForKey:COLUMN_ID];
}

它过滤正常,但循环和彩色球不断旋转

任何人都可以提供一个易于实现的想法,而不是按照他在答案最后的建议重写代码

- (NSArray *)filteredArrayPDContent 
{
   .....
   ....
于 2012-12-29T13:52:17.457 回答
0

你写过

NSArray *arr = [arrayPDContent filteredArrayUsingArrayPredicate:predicate];

由于-filteredArrayUsingArrayPredicate:不是方法 onNSMutableArray并且“filteredArrayUsingArrayPredicate”一词的唯一搜索结果是您使用相同内容打开的两个问题,我将假设这是-filteredArrayUsingPredicate:.


你正在使用-[NSArray filteredArrayUsingPredicate:]你的NSMutableArray arrayPDContent并且你期望这会发生变异arrayPDContent。这不是什么-filteredArrayUsingPredicate:;这是一个方法,NSArray并且在所有 s 上具有相同的行为NSArray,包括NSMutableArrays。我们可以想象它的实现是这样的:

- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate
{
    NSMutableArray *mutableRes = [NSMutableArray array];
    for (id obj in self) {
        if ([predicate evalutateWithObject:obj]) {
            [mutableRes addObject:obj];
        }
    }
    return [mutableRes copy];
}

特别是,这-filteredArrayUsingPredicate:不会改变其目标,即使该目标是NSMutableArray.

立即解决您的问题的方法是写

arrayPDContent = [[arrayPDContent filteredArrayUsingPredicate:predicate] mutableCopy];

代替

NSArray *arr = [arrayPDContent filteredArrayUsingPredicate:predicate];

但是,这不是一个好的解决方案,因为这样做会破坏数据。此处过滤掉的任何对象arrayPDContent都将丢失。

更好的解决方案是将谓词存储为属性

@property (nonatomic) NSPredicate *filter;

并更改您的实现-numberOfRowsInTableView:-tableView:objectValueForTableColumn:row:使用新属性:

- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [[arrayPDContent filteredArrayUsingPredicate:[self filter]] count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[[arrayPDContent filteredArrayUsingPredicate:[self filter]] objectAtIndex:row] objectForKey:COLUMN_ID];
}

而不是

- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    return [arrayPDContent count];
}

- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)column row:(NSUInteger)row
{
    return [[arrayPDContent objectAtIndex:row] objectForKey:COLUMN_ID];
}

这也不是一个很好的解决方案,因为过滤后的数组正在重复计算。更好的解决方案是缓存数组,添加属性

@property (nonatomic) NSArray *filteredArrayPDContent;

使用 getter 实现到您的表视图的委托

- (NSArray *)filteredArrayPDContent
{
    if (!_filteredArrayPDContent) {
        _filteredArrayPDContent = [arrayPDContent filteredArrayUsingPredicate:[self filter]];
    }
    return _filteredArrayPDContent;
}

并设置_filteredArrayPDContentnil无论何时arrayPDContent[self filter]更改。

编辑,附加

为了让您在任何时候或更改时参与设置_filteredArrayPDContentnilarrayPDContent可以[self filter]添加该方法

- (void)resetFilteredArrayPDContent
{
    _filteredArrayPDContent = nil;
}

由于应该从您 mutatearrayPDContent或 change的任何位置[self filter]调用此方法,因此您应该在以下位置调用此方法:

1.-filterOnClick:

- (IBAction)filterOnClick(id)sender 
{
    NSString *age = @"62";
    [self setFilter:[NSPredicate predicateWithFormat:@"Age >= ", age]];
    [self resetFilteredArrayPDContent];

    [gridView reloadData];
}

2. 填充后arrayPDContent

for (/*...*/) {
//...
    NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....];
    NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil];
    [arrayPDContent addObject:dictionary];
//...
}
[self resetFilteredArrayPDContent];

无论您是直接设置实例变量还是调用方法 [self setArrayPDContent:] (无论是使用标准括号表示法还是新的点表示法) ,您都需要调用-resetFilteredArrayPDContent (A ) 和(B)您进行变异(通过、、或任何其他对数组进行变异的方法)。[self arrayPDContent]self.arrayPDContent = //...arrayPDContent-addObject:-removeObject:-filterUsingPredicate:NSMutableArray

编辑继续,替代

代替1.上述内容,您可以更改以下的实现-setFilter:

- (void)setFilter:(NSPredicate *)filter
{
    _filter = filter;
    [self resetFilteredArrayPDContent];
}

并确保您正在调用-setFilter:(无论是使用括号[self setFilter:filter]-- -- 还是点-- --notation ),而不是直接在其他地方更改self.filter = filter实例变量。_filter-filterOnClick:[self filter]

代替2.上面,您可以添加方法

- (void)addArrayPDContentObject:(id)obj
{
    [arrayPDContent addObject:obj];
    [self resetFilteredArrayPDContent];
}

并在填充时调用它arrayPDContent

for (/*...*/) {
//...
    NSString *sAge = [NSString stringWithUTFString:sqlite3_column ....];
    NSDictionary *dictionary = [NSDictionary dictionaryWithObjectAndKeys:sAge, COLUMN_ID, nil];
    [self addArrayPDContentObject:dictionary];
//...
}

您需要为其他类型的突变添加类似的方法arrayPDContent

arrayPDContent如果在特定索引处插入对象,请添加

- (void)insertObject:(id)obj inArrayPDContentAtIndex:(NSUInteger)index
{
    [arrayPDContent insertObject:obj atIndex:index];
    [self resetFilteredArrayPDContent];
}

并打电话

[self insertObject:obj inArrayPDContentAtIndex:index];

而不是

[arrayPDContent insertObject:obj atIndex:index];

如果从arrayPDContent特定索引处删除对象

- (void)removeObjectFromArrayPDContentAtIndex:(NSUInteger)index
{
    [arrayPDContent removeObjectAtIndex:index];
    [self resetFilteredArrayPDContent];
}

并打电话

[self removeObjectFromArrayPDContentAtIndex:index];

而不是

[arrayPDContent removeObjectAtIndex:index];

等等。这些方法的命名约定可以在 Apple 的文档中找到。

编辑,其他选择

该问题的最简单但最糟糕的解决方案是缓存以前的版本,arrayPDContent并且filter每当计算_filteredArrayPDContent和检查当前版本与以前的版本。添加属性后

@property (nonatomic) NSArray *oldArrayPDContent;
@property (nonatomic) NSPredicate *oldFilter;

改变实现-filteredArrayPDContent

- (NSArray *)filteredArrayPDContent
{
    if (![[self oldArrayPDContent] isEqualToArray:arrayPDContent] 
        || ![[[self oldFilter] predicateFormat] isEqualToString:[[self filter] predicateFormat]]) {
        _filteredArrayPDContent = [arrayPDContent filteredArrayUsingPredicate:[self filter]];
        [self setOldFilter:[self filter]];
        [self setOldArrayPDContent:[arrayPDContent copy]];
    }
    return _filteredArrayPDContent;
}

请注意, oldArrayPDContent设置.arrayPDContent

我必须重申:这可能是提高性能的最快的替代品,但无论如何它都不是最时尚的。


更好的解决方案仍然是为NSArrayController您的内容数组添加适当的绑定(请注意,内容数组需要是不可变的才能使其工作 - 更准确地说,如果不小心,您将无法使用-addObject:其他NSMutableArray方法)和绑定你NSTableView的行到NSArrayController适当的位置。

使用这种风格,您只需-setFilterPredicate:调用NSArrayControllerfrom -filterOnClick:

于 2012-12-29T06:51:29.247 回答
0

NSArray方法filteredArrayUsingPredicate:创建一个新数组,该数组仅包含接收器中通过过滤器的那些对象。您正在使用局部变量来保存该数组,但是当它在您的方法arr结束时超出范围时会被丢弃(或至少变得不可访问) 。filterOnClick:大概您的数据源方法仍然引用arrayPDContent,因此您的表显示未过滤的数组。

如果您想更改 a 的内容以NSMutableArray使其仅包含与过滤器匹配的那些对象,则可以filterUsingPredicate:改为。(但是,请注意,这会丢弃任何其他内容——如果您想在用户清除过滤器后再次显示原始数据,则需要将其存储在其他地方。)

您可能会考虑使用一个 ivar/property 来存储“真实”数据,另一个来存储过滤结果(使用创建filteredArrayUsingPredicate:);如果已设置过滤器,则让表数据源方法引用后者。

于 2012-12-29T06:48:23.830 回答