无法为NSPredicateEditor设置带NOT运算符的单个子谓词
解决NSPredicateEditor显示“None”行搭配单个子行的问题
问题原因
NSPredicateEditor的NSNotPredicateType复合行模板默认要求子谓词必须是AND/OR类型的复合谓词,而非单个基础比较谓词。直接用NOT包裹单个简单谓词时,编辑器无法匹配到对应的模板结构,因此抛出异常。
解决方案
方法一:将单个子谓词包装为复合谓词(简单直接)
把需要的单个基础谓词用AND或OR复合谓词包裹后再套上NOT,就能匹配现有模板结构:
// 方式1:用NSCompoundPredicate构建 NSPredicate *singleSubPredicate = [NSPredicate predicateWithFormat:@"sampleName CONTAINS ''"]; // 用AND包裹单个子谓词(OR也可) NSCompoundPredicate *compoundSubPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[singleSubPredicate]]; editor.objectValue = [NSCompoundPredicate notPredicateWithSubpredicate:compoundSubPredicate]; // 方式2:直接用谓词格式字符串(括号包裹单个谓词,隐式创建复合结构) editor.objectValue = [NSPredicate predicateWithFormat:@"NOT (sampleName CONTAINS '')"];
这种方式会渲染出“None”(对应NOT)行,下方嵌套一个“All”(对应AND)行,再往下是单个基础条件行,能实现你需要的“None + 单个子行”逻辑。
方法二:自定义支持NOT直接包裹简单谓词的行模板(精准匹配)
如果不想多出中间的AND/OR层,可自定义行模板,让NOT模板直接支持单个基础谓词作为子项:
@interface NotWithSinglePredicateRowTemplate : NSPredicateEditorRowTemplate @property (nonatomic, strong) NSPredicateEditorRowTemplate *baseTemplate; @end @implementation NotWithSinglePredicateRowTemplate - (instancetype)initWithBaseTemplate:(NSPredicateEditorRowTemplate *)baseTemplate { self = [super init]; if (self) { _baseTemplate = baseTemplate; } return self; } // 匹配NOT包裹单个简单谓词的结构 - (BOOL)matchesPredicate:(NSPredicate *)predicate { if (![predicate isKindOfClass:[NSCompoundPredicate class]]) return NO; NSCompoundPredicate *compound = (NSCompoundPredicate *)predicate; if (compound.compoundPredicateType != NSNotPredicateType) return NO; if (compound.subpredicates.count != 1) return NO; NSPredicate *subPredicate = compound.subpredicates.firstObject; return [self.baseTemplate matchesPredicate:subPredicate]; } // 生成对应的NOT谓词 - (NSPredicate *)predicateWithSubpredicates:(NSArray<NSPredicate *> *)subpredicates { if (subpredicates.count != 1) return nil; return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; } // 指定子模板数量 - (NSInteger)numberOfSubpredicates { return 1; } // 返回对应的基础子模板 - (NSPredicateEditorRowTemplate *)templateForSubpredicateAtIndex:(NSInteger)index { if (index == 0) return self.baseTemplate; return nil; } @end
修改模板初始化代码,替换原有的复合模板:
// 创建基础模板 NSMutableArray *finalTemplates = [NSMutableArray arrayWithCapacity:2]; for(NSString *keyPath in @[@"sampleName",@"comment"]) { NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions:@[[NSExpression expressionForKeyPath:keyPath]] rightExpressionAttributeType:NSStringAttributeType modifier:NSDirectPredicateModifier operators:@[@(NSContainsPredicateOperatorType), @(NSBeginsWithPredicateOperatorType)] options: 0]; [finalTemplates addObject:template]; } // 创建自定义NOT+单个基础模板 NSMutableArray *customCompoundTemplates = [NSMutableArray array]; for (NSPredicateEditorRowTemplate *baseTemplate in finalTemplates) { NotWithSinglePredicateRowTemplate *notTemplate = [[NotWithSinglePredicateRowTemplate alloc] initWithBaseTemplate:baseTemplate]; [customCompoundTemplates addObject:notTemplate]; } // 添加原有的AND/OR复合模板 NSArray *compoundTypes = @[@(NSAndPredicateType), @(NSOrPredicateType)]; NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:compoundTypes]; [customCompoundTemplates addObject:compound]; // 设置编辑器模板 editor.rowTemplates = customCompoundTemplates;
现在直接使用NOT sampleName CONTAINS ''这类谓词,编辑器会直接渲染“None”行搭配单个基础子行,无多余中间层。
补充说明
你之前测试的NOT (sampleName CONTAINS '' OR comment CONTAINS '')能正常工作,是因为括号内的OR是复合谓词,符合默认NOT模板对“子谓词为复合类型”的要求,因此编辑器能匹配到对应模板结构。
内容的提问来源于stack exchange,提问作者jeanlain
相关产品推荐
相关产品推荐

