我正在尝试生成一个NSPredicateEditorRowTemplate
,左侧有一个来自实体 Foo 的多个属性名称,其中包含属性 bar 。当用户选择“ bar ”时,右侧应该成为弹出窗口,其中包含“ bar ”的所有值。
如何才能最好地填充右侧弹出窗口? bar 的所有唯一值都存储在NSMutableArray
中,因此我可以使用KVO在数组更改时更改行模板。
有没有办法可以使用代码轻松更改NSPredicateEditor
行右侧弹出窗口中的值?我可以在IB中输入一些静态值,但在这种情况下不会这样做。
修改
阅读了大量相关的Q& A,包括NSPredicateEditor in Xcode 4以及@Dave DeLong的优秀答案,我认为可以做的很多工作如下:
NSArray *leftexp = @[[NSExpression expressionForKeyPath:@"name"],[NSExpression expressionForKeyPath:@"married"]];
NSArray *rightexp = @[[NSExpression expressionWithFormat:@"One"],[NSExpression expressionWithFormat:@"Two"]];
NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions:leftexp rightExpressions:rightexp modifier:NSDirectPredicateModifier operators:@[@(NSEqualToPredicateOperatorType)] options:0];
NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:@[@(NSOrPredicateType)]];
[self.predicateEditor setRowTemplates:@[template,compound]];
NSEntityDescription *description = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:self.managedObjectContext];
self.predicate = [NSPredicate predicateWithFormat:@"name == ''"];
我已经看到了一些方法来使基本的NSPredicateEditor出现(至少有复合线),但在我看来,必须有一种优雅的方式来做到这一点,它的方式是意味着待完成。虽然找不到,但有人可以帮我这个吗?
答案 0 :(得分:4)
有些人可能认为意味着完成的方式是在Interface Builder中。如果您熟悉行模板的工作方式,它们如何组合等等,这可能会很好。就个人而言,我更喜欢以编程方式创建,因为我可以明确说明行模板的功能。
让我们看看这一切如何协同工作的一个例子,然后你可以决定哪种方法最适合你:
NSString
属性的对象:bar
和baz
bar
只能包含一定数量的可能值,我们称之为@"Bar1"
,@"Bar2"
和@"Bar3"
。baz
可以是任何NSString
值。考虑到这一点,以下是您以编程方式创建的方式:
// these defines are for brevity on the web page; please don't actually do this
#define NSPERT NSPredicateEditorRowTemplate
#define KP(_kp) [NSExpression expressionForKeyPath:(_kp)]
#define CV(_cv) [NSExpression expressionForConstantValue:(_cv)]
NSPERT *compound = [[NSPERT alloc] initWithCompoundTypes:@[@(NSAndPredicateType),
@(NSOrPredicateType),
@(NSNotPredicateType)]];
NSPERT *bar = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"bar")]
rightExpressions:@[CV(@"Bar1"), CV(@"Bar2"), CV(@"Bar3")]
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSPERT *baz = [[NSPERT alloc] initWithLeftExpressions:@[KP(@"baz")]
rightExpressionAttributeType:NSStringAttributeType
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType),
@(NSNotEqualToPredicateOperatorType)]
options:0];
NSArray *templates = @[compound, bar, baz];
[self.myPredicateEditor setRowTemplates:templates];
当然,这只是为了创建行模板而进行的大量输入。我明白那个。我只是认为它也非常明确且易于调试。话虽这么说,您也可以在Interface Builder中配置完全相同的东西。为此,请在xib中放置NSPredicateEditor
,并为其提供3行模板。您将单独留下的复合行模板,但另外两个您将更改为如下配置:
对于“bar
”模板:
对于“baz
”模板:
如果您将这些屏幕截图与上面的代码进行比较,它应该(希望)很容易看到一个映射到另一个。使用这样配置的行模板,IB还会在窗口中显示谓词编辑器:
如果您想要执行更高级的操作,例如创建自定义行模板,或者使用数据模型中的某些内容填充允许的常量值列表,那么您将不得不放弃在代码中进行配置。
编辑以回答评论问题
这些选择器的modifier
位对应于创建的NSComparisonPredicate
的{{3}}。有三个修饰符:Direct,Any和All。
@"bar = 'Bar1'";
^ No modifier means this is NSDirectPredicateModifier
@"ANY user.userName = 'markjs'";
^~~ use of "ANY" keyword means this is NSAnyPredicateModifier
@"ALL user.age < 42";
^~~ use of "ALL" keyword means this is NSAllPredicateModifier
在这三个例子中,它们都是比较谓词(因为它们有左侧,操作符和右侧),但使用“ANY”或“ALL”会影响左侧的解释方式。如果存在任何一个,则谓词期望左侧评估为可能的集合。
使用“直接”选项(或0
)基本上意味着您将进行一对一的比较。
答案 1 :(得分:3)
使用复合行(NOT,AND,OR)填充NSPredicateEditor
以及使用简单谓词填充一行的关键是NSPredicateEditor
应该收到-setRowTemplates:
使用包含复合和常规谓词的NSArray
调用。然后,应使用-andPredicateWithSubpredicates:
将其谓词设置为具有子预测的复合谓词。
这对我有用:
NSArray *leftexp = @[[NSExpression expressionForKeyPath:@"name"]];
NSArray *rightexp = @[[NSExpression expressionForConstantValue:@"A"],[NSExpression expressionForConstantValue:@"B"],[NSExpression expressionForConstantValue:@"C"]];
NSPredicateEditorRowTemplate *template = [[NSPredicateEditorRowTemplate alloc] \
initWithLeftExpressions:leftexp
rightExpressions:rightexp
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType)]
options:0];
NSPredicateEditorRowTemplate *compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes:@[@(NSAndPredicateType),@(NSOrPredicateType),@(NSNotPredicateType)]];
NSMutableArray *templates = [NSMutableArray arrayWithObject:compound];
[templates addObject:template];
[self.predicateEditor setRowTemplates:templates];
NSPredicate *p = [NSPredicate predicateWithFormat:@"name == 'A'"];
self.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObject:p]];
答案 2 :(得分:0)
基于markjs回答,我将其翻译为swift 4并将其放在NSPredicateEditorRowTemplate的扩展名中。
extension NSPredicateEditorRowTemplate {
convenience init( compoundTypes: [NSCompoundPredicate.LogicalType] ) {
var compoundTypesNSNumber: [NSNumber] = []
for c in compoundTypes { compoundTypesNSNumber.append( NSNumber(value: c.rawValue) ) }
self.init( compoundTypes: compoundTypesNSNumber )
}
convenience init( forKeyPath keyPath: String, withValues values: [Any] ) {
let keyPaths: [NSExpression] = [NSExpression(forKeyPath: keyPath)]
var constantValues: [NSExpression] = []
for v in values {
constantValues.append( NSExpression(forConstantValue: v) )
}
let operators: [NSComparisonPredicate.Operator] = [.equalTo, .notEqualTo]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: keyPaths,
rightExpressions: constantValues,
modifier: .direct,
operators: operatorsNSNumber,
options: (Int(NSComparisonPredicate.Options.caseInsensitive.rawValue | NSComparisonPredicate.Options.diacriticInsensitive.rawValue)) )
}
convenience init( stringCompareForKeyPaths keyPaths: [String] ) {
var leftExpressions: [NSExpression] = []
for keyPath in keyPaths {
leftExpressions.append( NSExpression(forKeyPath: keyPath) )
}
let operators: [NSComparisonPredicate.Operator] = [.contains, .equalTo, .notEqualTo, .beginsWith, .endsWith]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: leftExpressions,
rightExpressionAttributeType: .stringAttributeType,
modifier: .direct,
operators: operatorsNSNumber,
options: (Int(NSComparisonPredicate.Options.caseInsensitive.rawValue | NSComparisonPredicate.Options.diacriticInsensitive.rawValue)) )
}
convenience init( IntCompareForKeyPaths keyPaths: [String] ) {
var leftExpressions: [NSExpression] = []
for keyPath in keyPaths {
leftExpressions.append( NSExpression(forKeyPath: keyPath) )
}
let operators: [NSComparisonPredicate.Operator] = [.equalTo, .notEqualTo, .greaterThan, .greaterThanOrEqualTo, .lessThan, .lessThanOrEqualTo]
var operatorsNSNumber: [NSNumber] = []
for o in operators { operatorsNSNumber.append( NSNumber(value: o.rawValue) ) }
self.init( leftExpressions: leftExpressions,
rightExpressionAttributeType: .integer16AttributeType,
modifier: .direct,
operators: operatorsNSNumber,
options: 0 )
}
}
使用它如下:
func setMyPetFilter() {
// We assume that myPredicateEditor is create/set in IB
// Remove whatever was configured in IB
myPredicateEditor.rowTemplates.removeAll()
let templateCompoundTypes = NSPredicateEditorRowTemplate( compoundTypes: [.and, .or, .not] )
let template1 = NSPredicateEditorRowTemplate( forKeyPath: "Animals", withValues: ["Dog", "Cat", "Hamster", "Canary"] )
let template2 = NSPredicateEditorRowTemplate( stringCompareForKeyPaths: ["PetName", "OwnerName"] )
let template3 = NSPredicateEditorRowTemplate( forKeyPath: "Legs", withValues: [2,4] )
let template4 = NSPredicateEditorRowTemplate( IntCompareForKeyPaths: ["Age", "AgeOwner"] )
myPredicateEditor.rowTemplates = [templateCompoundTypes, template1, template2, template3, template4]
}