我在带有数据行的表单上有一个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内部登录,我输入了以上代码的部分内容
答案 0 :(得分:0)
NSArray
方法filteredArrayUsingPredicate:
创建一个新数组,其中只包含接收器中传递过滤器的那些对象。您正在使用局部变量arr
来保存该数组,但当它在filterOnClick:
方法结束时超出范围时,它将被丢弃(或至少变得无法访问)。据推测,您的数据源方法仍然引用arrayPDContent
,因此您的表显示了未过滤的数组。
如果要更改NSMutableArray
的内容,使其仅包含与过滤器匹配的对象,则需要filterUsingPredicate:
。 (但请注意,这会丢弃其他任何内容 - 如果您想在用户清除过滤器后再次显示原始数据,则需要将其存储在其他位置。)
您可以考虑使用一个ivar / property来存储“真实”数据,另一个用于存储过滤结果(使用filteredArrayUsingPredicate:
创建);如果已设置过滤器,则表数据源方法引用后者。
答案 1 :(得分:0)
你已经写过了
NSArray *arr = [arrayPDContent filteredArrayUsingArrayPredicate:predicate];
由于-filteredArrayUsingArrayPredicate:
不是NSMutableArray
上的一种方法,而且该术语的唯一搜索结果是" filteredArrayUsingArrayPredicate"您使用相同内容打开的两个问题,我将假设这是-filteredArrayUsingPredicate:
的拼写错误。
您在-[NSArray filteredArrayUsingPredicate:]
NSMutableArray
上使用arrayPDContent
,并且您预计这会改变arrayPDContent
。这不是-filteredArrayUsingPredicate:
所做的;这是NSArray
上的一种方法,在所有NSArray
上都有相同的行为,包括NSMutableArray
。我们可以想象它的实现类似于以下内容:
- (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;
}
并在_filteredArrayPDContent
或nil
更改时将arrayPDContent
设置为[self filter]
。
修改,附加:
为了让您在_filteredArrayPDContent
或nil
更改时将arrayPDContent
设置为[self filter]
,您可以添加方法
- (void)resetFilteredArrayPDContent
{
_filteredArrayPDContent = nil;
}
由于应该从您变异arrayPDContent
或更改[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];
每当设置-resetFilteredArrayPDContent
的值时,无论是直接设置实例变量还是调用方法,都需要调用[self arrayPDContent]
(A) [self setArrayPDContent: ](无论是使用标准括号表示法还是使用新的点符号self.arrayPDContent = //...
)和(B),只要你改变arrayPDContent
(通过-addObject:
,{{1 } {},-removeObject:
或-filterUsingPredicate:
上的任何其他变异数组的方法。)
编辑续,替代:
代替上面NSMutableArray
,您可以改为改变1.
的实施:
-setFilter:
并确保您正在调用- (void)setFilter:(NSPredicate *)filter
{
_filter = filter;
[self resetFilteredArrayPDContent];
}
(无论是使用括号 - -setFilter:
- 还是点 - [self setFilter:filter]
- 符号)而不是设置实例变量{{1直接在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];
}
等等。这些方法的命名约定是here in Apple's documentation。
修改,其他选择:
问题的最简单但最糟糕的解决方案是在计算[self removeObjectFromArrayPDContentAtIndex:index];
并检查当前版本时缓存先前版本的[arrayPDContent removeObjectAtIndex:index];
和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:
即可。
答案 2 :(得分: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
{
.....
....