I am trying to generate an NSPredicateEditorRowTemplate
that on the left side has a number of property names from an entity Foo, among which is the property bar. When the user selects 'bar', the right side should become popup which contains all values of 'bar'.
How can I best populate the right side popup? All unique values of bar are stored in an NSMutableArray
, so perhaps I can use KVO to change the row template when the array changes.
Is there a way that I can use code to easily change the values in a right side popup in an NSPredicateEditor
row? I can enter a few static values in IB, but that will not do in this situation.
EDIT
Having read a good deal of related Q&A's, including NSPredicateEditor in Xcode 4 and the excellent answer to it by @Dave DeLong, I think a good bit of the work can be done like this:
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 == ''"];
I've seen a few ways to make the basic NSPredicateEditor appear (with at least the compound line), but it seems to me that there has to be an elegant way to do that, the way it was meant to be done. Can't find that though, can anyone help me along with this?
Some would probably argue that the way it is meant to be done is in Interface Builder. That would probably be fine if you're familiar with how row templates work, how they get combined, etc. Personally, I prefer to create the programmatically because I can be explicit about how the row templates function.
Let's go through an example of how this all works together, and then you can decide which approach works best for you:
NSString
properties: bar
and baz
bar
can only have a certain number of possible values, which we'll call @"Bar1"
, @"Bar2"
, and @"Bar3"
.baz
can be any NSString
value.With this in mind, here's how you'd create that programmatically:
// 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];
Granted, this is a lot of typing just to create row templates. I understand that. I just think that it's also very explicit and easy to debug. That being said, you can also configure this exact same thing in Interface Builder. To do so, put an NSPredicateEditor
in your xib, and give it 3 row templates. The Compound row template you'd leave alone, but the other two you'd change to be configured like this:
For the "bar
" template:
For the "baz
" template:
If you compare these screenshots to the code above, it should (hopefully) be easy to see how one maps to the other. With the row templates configured like this, IB also shows the predicate editor in your window:
If you want to do more advanced things, such as creating custom row templates, or populating the list of allowed constant values with something from your data model, then you'll have to drop to doing the configuration in code.
Edit to answer comment question
The modifier
bit of these selectors corresponds to the NSComparisonPredicateModifer
for the created NSComparisonPredicate
. There are three modifiers: Direct, Any, and 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
In these three examples, they're all comparison predicates (because they have a left side, an operator, and a right side), but the use of "ANY" or "ALL" affects how the left side is interpreted. If either is present, then the predicate is expecting the left-hand side to evaluate to a collection of possibilities.
Using the "direct" option (or 0
) basically means you're going to be doing a one-to-one comparison.