Predicate editor issue - objective-c

Inside my custom scroll view i have added predicate object as under. It is giving error in the methods predicateWithSubpredicates in debug stack. Here is my sample code please let me know if any error are present.
-(void) awakeFromNib
{
NSPredicateEditor *predicateeditor = [[NSPredicateEditor alloc] initWithFrame:NSMakeRect(0, 0, 200, 150)];
NSArray *leftExpressions = [NSArray arrayWithObjects:[NSExpression expressionForKeyPath:#"date"], nil];
NSAttributeType rightType = NSDateAttributeType;
NSComparisonPredicateModifier modifier = NSAllPredicateModifier; //don't need "ANY" or "ALL"
NSArray *operators = [NSArray arrayWithObjects:[NSString stringWithString:#"Today"],[NSString stringWithString:#"Tomorrow"],[NSString stringWithString:#"Next week"], nil];
NSUInteger options = 0;
NSPredicateEditorRowTemplate *rowTemplate = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions:leftExpressions rightExpressionAttributeType:rightType modifier:modifier operators:operators options:options];
[predicateeditor setRowTemplates:[NSArray arrayWithObject:rowTemplate]];
[rowTemplate release];
[self addSubview:predicateeditor];
[predicateeditor addRow:nil];
[predicateeditor displayValuesForRow:1];
[predicateeditor release];
}

As you've written it, this is going to define a predicate like this:
ALL date {"Today", "Tomorrow", "Next Week"} {a user-entered date}
I hope you can see that this is non-sensical.
For starters, if you really mean that //don't need "ANY" or "ALL", then you shouldn't be be using NSAllPredicateModifier. You should be using NSDirectPredicateModifier.
Also, the things that are allowed to go in the operators array are NSNumber objects that box one of the built-in predicate operator values.
So: what are you trying to accomplish?

Related

How to use NSArray in objective C. Card Deck on iPhone

I´m all new to Objective C and I can´t seem to find how to do this (if it´s even possible).
I have a NSArray filled with 4 Cards (Card = my own Class) and I want to set the text of a label to a NSString which my card objects holds.
In Java I would do something like this:
If I have an Card Array with 4 Cards and theLabel is a String.
theLabel = deck[2].getLabel();
This don´t seem to work on Objective C. My code in Objective C so far:
- (IBAction)nextCard:(id)sender {
theLabel.text = [deck objectAtIndex:j].getLabel;
theImage.image = [deck objectAtIndex:j].getImage;
}
nextCard calls every time I click on a button on the iPhone.
j is an ordinary int that will keep track on which card to show.
It looks like this when I am creating the array:
- (void)viewDidLoad
{
[super viewDidLoad];
ah = [[Card alloc]init];
[ah setLabel:#"label1"];
[ah setCardImage: [UIImage imageNamed:#"3.png" ]];
as = [[Card alloc]init];
[as setLabel:#"label2"];
[as setCardImage: [UIImage imageNamed:#"2.png" ]];
ac = [[Card alloc]init];
[ac setLabel:#"labbel3"];
[ac setCardImage: [UIImage imageNamed:#"1.png" ]];
ad = [[Card alloc]init];
[ad setLabel:#"label4"];
[ad setCardImage: [UIImage imageNamed:#"4.png" ]];
deck = [NSMutableArray arrayWithCapacity:25];
[deck addObject:ah];
[deck addObject:as];
[deck addObject:ac];
[deck addObject:ad];
}
So I would basically want to be able to use methods ob objects stores in a NSArray.
Would really appreciate answers, thanks in advance!
In objective-C the generated getter hasn't the get prefix, so you use it that way:
theLabel = deck[2].label;
Or:
theLabel= [deck[2] label];
Check this:
[deck objectAtIndex:0].label;
or:
[[deck objectAtIndex:0] label];
or:
[[deck objectAtIndex:0] yourMethodWithParameter:#"foo"];
In Objective-C, in most cases the accessor methods are auto generated.
Let us say we have a property named "object"
The getter method is not named like this getObject , where "object" is the property you want to access.
It is named simply by the name of the property like this object
So in your case it should be something like this:
NSString *theLabelString = [[deck objectAtIndex:j] label];
//or this:
NSString *theLabelString = [deck[j] label];
//or this:
NSString *theLabelString = deck[j].label;

"for x in" syntax with NSArray

I've this kind of instruction:
[self someMethod:CGPointMake(50, 50)];
[self someMethod:CGPointMake(270, 50)];
[self someMethod:CGPointMake(50, 360)];
[self someMethod:CGPointMake(270, 360)];
...
I want to refactor code using NSArray like this:
NSArray items = [NSArray initWithObjects:
CGPointMake(50, 50),
CGPointMake(270, 50),
CGPointMake(50, 360),
CGPointMake(270, 360),
...
nil];
I dont know right syntax, can someone help me? I'd tried with this, but XCode tells me "Selector element type CGPoint is not a valid object":
CGPoint point = [CGPoint alloc];
for (point in items) {
[self someMethod:point];
}
for-in loops are an Objective-C concept for iterating over collection classes (that conform to NSEnumeration). If you would like to iterate over C-structs (like CGPoints), use a standard for-loop with a C-array, or wrap the CGPoints in NSValues.
Here's what your refactoring would look like in modern Objective-C syntax:
NSArray *items = #[
[NSValue valueWithPoint:CGPointMake(50, 50)], //wrap the points in an
[NSValue valueWithPoint:CGPointMake(270, 50)], //NSValue so they become
[NSValue valueWithPoint:CGPointMake(50, 360)], //first class citizens
[NSValue valueWithPoint:CGPointMake(270, 360)],//(Y no boxing?*)
]; //End of collection literal
for (NSValue *value in items) { //iterate through the NSValues with our points
[self someMethod:[value pointValue]]; //'un-wrap' the points by calling -pointValue
}
*My personal struct boxing macro:
#define STRUCT_BOX(x) [NSValue valueWithBytes:&x objCType:#encode(typeof(x))];
There's no need to resort to NSArray. As CodaFi says, "If you would like to iterate over C-structs (like CGPoints), use a standard for-loop with a C-array." Well then, why not do so?
static CGPoint items[] = {
{50, 50},
{270, 50},
{50, 360},
{270, 360},
};
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))
This creates the array at compile-time, not run-time! Then iterate the array:
for (NSUInteger i = 0; i < ARRAY_SIZE(items); ++i)
[self someMethod:items[i]];
For another example involving an array of dictionaries, see Objective-C Is Still C (Not Java!)

How to efficiently access large objects in Obj-C using objectForKey and objectAtIndex?

If I have a large NSDirectory typically from a parsed JSON-object I can access this object with code like so:
[[[[[obj objectForKey:#"root"] objectForKey:#"key1"] objectAtIndex:idx] objectForKey:#"key2"] objectAtIndex:idz];
The line might be a lot longer than this.
Can I optimize this code in any way? At least make it easier to read?
This line will also generate a runtime-error if the object does not correspond, what is the most efficient way to avoid that?
If you were using -objectForKey: for everything you could use -valueForKeyPath:, as in
[obj valueForKeyPath:#"key1.key2.key3.key4"]
However, this doesn't work when you need to use -objectAtIndex:. I don't think there's any good solution for you. -valueForKeyPath: also wouldn't solve the problem of the runtime errors.
If you truly want a simple way to do this you could write your own version of -valueForKeyPath: (call it something else) that provides a syntax for specifying an -objectAtIndex: instead of a key, and that does the appropriate dynamic checks to ensure the object actually responds to the method in question.
If you want easier to read code you can split the line into several lines like this
MyClass *rootObject = [obj objectForKey:#"root"];
MyClass *key1Object = [rootObject objectForKey:#"key1"];
MyClass *myObject = [key1Object objectAtIndex:idx];
...
and so forth.
I think, you can create some array, that will contain full "path" to your object. The only thing, you need to store your indexes somehow, maybe in NSNumber, in this case you cannot use NSNumber objects as keys in your dictionaries. Then create a method, that will return needed object for this given "path". smth like
NSMutableArray* basePath = [NSMutableArray arrayWithObjects: #"first", [NSNumber numberWithInt:index], nil];
id object = [self objectForPath:basePath inContainer:container];
- (id) objectForPath:(NSMutableArray*)basePath inContainer:(id)container
{
id result = nil;
id pathComponent = [basePath objectAtIndex: 0];
[basePath removeObjectAtIndex: 0];
// check if it is a number with int index
if( [pathComponent isKindOfClass:[NSNumber class]] )
{
result = [container objectAtIndex: [pathComponent intValue]];
}
else
{
result = [container objectForKey: pathComponent];
}
assert( result != nil );
// check if it is need to continue searching object
if( [basePath count] > 0 )
{
return [self objectForPath:basePath inContainer: result];
}
else
{
return result;
}
}
this is just an idea, but I hope you understand what I mean. And as Kevin mentioned above, if you don't have indexes, you can use key-value coding.
Don't know if it can suit you, but you could also give a try to blocks, I always find them very convenient. At least they made code much more readable.
NSArray *filter = [NSArray arrayWithObjects:#"pathToFind", #"pathToFind2",nil];
NSPredicate *filterBlock = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSArray *root = (NSArray*)obj;
// cycle the array and found what you need.
// eventually implementing some sort of exit strategy
}];
[rootObject filteredArrayUsingPredicate:filterBlock];

Using NSPredicate to analyze strings

Howdy! This question is pretty long so be sure to have a seat first :)
At one point in my code I get a string from the user, and I analyze that string. By analyzing I mean going through every character and mathcing it against a predicate the user has set in an NSPredicateEditor. The predicate is setup progamatically this way:
NSArray* keyPaths = [NSArray arrayWithObjects:
[NSExpression expressionForKeyPath: #"Character"],
[NSExpression expressionForKeyPath: #"Character Before"],
[NSExpression expressionForKeyPath: #"Character After"],
nil];
// -----------------------------------------
NSArray* constants = [NSArray arrayWithObjects:
[NSExpression expressionForConstantValue: #"Any letter"],
[NSExpression expressionForConstantValue: #"Letter (uppercase)"],
[NSExpression expressionForConstantValue: #"Letter (lowercase)"],
[NSExpression expressionForConstantValue: #"Number"],
[NSExpression expressionForConstantValue: #"Alphanumerical"],
[NSExpression expressionForConstantValue: #"Ponctuation Mark"],
nil];
// -----------------------------------------
NSArray* compoundTypes = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSNotPredicateType],
[NSNumber numberWithInteger: NSAndPredicateType],
[NSNumber numberWithInteger: NSOrPredicateType],
nil];
// -----------------------------------------
NSArray* operatorsA = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSEqualToPredicateOperatorType],
[NSNumber numberWithInteger: NSNotEqualToPredicateOperatorType],
nil];
NSArray* operatorsB = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSInPredicateOperatorType],
nil];
// -----------------------------------------
NSPredicateEditorRowTemplate* template1 = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions: keyPaths
rightExpressions: constants
modifier: NSDirectPredicateModifier
operators: operatorsA
options: 0];
NSPredicateEditorRowTemplate* template2 = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions: keyPaths
rightExpressionAttributeType: NSStringAttributeType
modifier: NSDirectPredicateModifier
operators: operatorsB
options: 0];
NSPredicateEditorRowTemplate* compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes: compoundTypes];
// -----------------------------------------
NSArray* rowTemplates = [NSArray arrayWithObjects: template1, template2, compound, nil];
[myPredicateEditor setRowTemplates: rowTemplates];
So you can see I have three keypaths and some constants they can be compared with.
When analyzing the string I basically want to do this, in pseudocode:
originalString = [string from NSTextView]
for (char in originalString)
bChar = [character before char]
aChar = [character after char]
predicate = [predicate from myPredicateEditor]
// using predicate - problem!
result = [evaluate predicate with:
bChar somehow 'linked' to keypath 'Character Before'
char 'linked' to 'Character'
aChar 'linked' to 'Character After' // These values change, of course
and also:
constant "All letters" as "abc...WXYZ"
constant "Numbers" as "0123456789"
etc for all other constants set up // These don't
]
if (result) then do something with char, bChar and aChar
You can see where my problem basically lies:
'Character Before/After' cannot be keypaths because of the space, but I want to keep it that way as it is more beautiful for the user (imagine having something as 'characterBefore' instead...)
Constants such as 'Numbers' actually represent strings like '0123456789', witch I can't display to the user as well
I was able to find a workaround to this problem, but I now it doesn't work with every character and it is also very unefficient (in my opinion, at least). What I do is get the predicate format from the predicate, replace what I have to replace, and evaluate that new format instead. Now for some real code that explains this:
#define kPredicateSubstitutionsDict [NSDictionary dictionaryWithObjectsAndKeys: \
#"IN 'abcdefghijklmnopqrstuvwxyz'", #"== \"Letter (lowercase)\"", \
#"IN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'", #"== \"Letter (uppercase)\"", \
#"IN 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'", #"== \"Any letter\"", \
#"IN '1234567890'", #"== \"Number\"", \
#"IN 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'", #"== \"Alphanumerical\"", \
#"IN ',.;:!?'", #"== \"Ponctuation Mark\"", \
\
#"MATCHES '[^a-z]'" , #"!= \"Letter (lowercase)\"", \
#"MATCHES '[^A-Z]'" , #"!= \"Letter (uppercase)\"", \
#"MATCHES '[^a-zA-Z]'" , #"!= \"Any letter\"", \
#"MATCHES '[^0-9]'" , #"!= \"Number\"", \
#"MATCHES '[^a-zA-Z0-9]'" , #"!= \"Alphanumerical\"", \
#"MATCHES '[^,\.;:!\?]'" , #"!= \"Ponctuation Mark\"", \
\
nil]
// NSPredicate* predicate is setup in another place
// NSString* originalString is also setup in another place
NSString* predFormat = [predicate predicateFormat];
for (NSString* key in [kPredicateSubstitutionsDict allKeys]) {
prefFormat = [predFormat stringByReplacingOccurrencesOfString: key withString: [kPredicateSubstitutionsDict objectForKey: key]];
}
for (NSInteger i = 0; i < [originalString length]; i++) {
NSString* charString = [originalString substringWithRange: NSMakeRange(i, 1)];
NSString* bfString;
NSString* afString;
if (i == 0) {
bfString = #"";
}
else {
bfString = [originalString substringWithRange: NSMakeRange(i - 1, 1)];
}
if (i == [originalString length] - 1) {
afString = #"";
}
else {
afString = [originalString substringWithRange: NSMakeRange(i + 1, 1)];
}
predFormat = [predFormat stringByReplacingOccurrencesOfString: #"Character Before" withString: [NSString stringWithFormat: #"\"%#\"", bfString]];
predFormat = [predFormat stringByReplacingOccurrencesOfString: #"Character After" withString: [NSString stringWithFormat: #"\"%#\"", afString]];
predFormat = [predFormat stringByReplacingOccurrencesOfString: #"Character" withString: [NSString stringWithFormat: #"\"%#\"", charString]];
NSPredicate* newPred = [NSPredicate predicateWithFormat: predFormat];
if ([newPred evaluateWithObject: self]) { // self just so I give it something (nothing is actually gotten from self)
// if predicate evaluates to true, then do something exciting!
}
}
So, here you go, this is a simplified version of what I am doing. If you see any typos, most probably they're not in my code, because I've edited this quite a bit so it would be simpler.
To summarize:
I need to evaluate the predicate the user makes against many characters, modifying it quite a bit, while trying to be as efficient as possible
The problems I find with my approach are:
I don't think it's clean at all
I have no guarantee that it will work on every case (when one of the character is a newline/enter character, the predicate raises an error saying it can't understand the format)
That's all folks! Thanks for reading thus far. May your god be with you when solving this mess!
EDIT: Just to clarify things abut, I would add that what seems the trigger to this problem is the fact that I cannot, right at the start when I setup the predicate editor, define one constant with a name (that gets displayed to the user) and a value that represents that constant and gets inserted in the predicate format. The same thing for keypaths: if I could have one display name, and then one value that would be those var strings for predicate ($VAR or whatever it is) all the problems would be solved. If this is possible, please tell me how. If it is impossible, then please focus on the other problems I describe.
So the main question:
Can you build an NSPredicateEditorRowTemplate that uses different constants than what's displayed in the user interface?
Answer: yes. You used to be able to set this up in Interface Builder, but my attempts to do so in Xcode 4 have been unsuccessful. It's possible that the capability was removed in the IB => Xcode 4 merge.
Fortunately, you can do it in code. Unfortunately, it requires an NSPredicateEditorRowTemplate subclass. I haven't found a way to do it without such a subclass.
The gist is to override -templateViews in your row template, and do something like this:
- (NSArray *)templateViews {
NSArray *views = [super templateViews];
// views[0] is the left-hand popup
// views[1] is the operator popup
// views[2] is the right-hand side (textfield, popup, whatever)
NSPopUpButton *left = [views objectAtIndex:0];
NSArray *items = [left itemArray];
for (NSMenuItem *item in items) {
//the representedObject of the item is the expression that will be used in the predicate
NSExpression *keyPathExpression = [item representedObject];
//we can alter the -title of the menuItem without altering the expression object
if ([[keyPathExpression keyPath] isEqual:#"before"]) {
[item setTitle:#"Character Before"];
} else if ([[keyPathExpression keyPath] isEqual:#"after"]) {
[item setTitle:#"Character After"];
}
}
return views;
}
And voilá! A popup in your predicate editor where the title does not match the keypath.
edit
The other way to solve this (without subclassing!) would be to put your custom text in a .strings file and "localize" your editor to English (or whatever language you want).
Localizing NSPredicateEditor
Followup to Localizing NSPredicateEditor
Imagine this situation: a user puts in the predicate editor this expression Character/is not/Number. The format for that predicate would be "character != "0123456789" This is always true, even if the character is a number! How do I "replace" these operators: is/is not with their real functions IN/NOT (IN ...)?
If you were to express these comparisons in a predicate, you probably wouldn't use != and ==. Likely, you'd want to do something like:
character MATCHES '[0-9]'
And one of:
character MATCHES '[^0-9]'
NOT(character MATCHES '[0-9]')
For this, the way that I can think of to do this would be to have two separate row templates. One would recognize and display predicates in the first format (MATCHES '[0-9]'), and the second would recognize and display the negation. You're starting to get into the weird realm of NSPredicateEditorRowTemplates, I think. I'm still not really sure what you're trying to accomplish and why you want to be matching every character of a string against a predicate, but whatever.
For more information on how to create custom row templates, check out these two blog posts:
Creating a simple NSPredicateEditorRowTemplate
Creating an advanced NSPredicateEditorRowTemplate
Why is the subclass necessary for setting the visual text? Wouldn't it be less expensive to simply create a method in your class like:
- (void)setVisibleMenuTitlesInPredicate:(NSPredicateEditorRowTemplate *)predicateTemplate
{
NSArray *predicateTemplateViews = [predicateTemplate templateViews];
NSPopUpButton *leftMenu = [predicateTemplateViews objectAtIndex:0];
NSArray *leftMenuList = [leftMenu itemArray];
for (NSMenuItem *menuItem in leftMenuList) {
id keyPathValue = [menuItem representedObject];
if ([[keyPathExpression keyPath] isEqual:#"before"]) {
[menuItem setTitle:#"Character Before"];
} else if ([[keyPathExpression keyPath] isEqual:#"after"]) {
[menuItem setTitle:#"Character After"];
}
}
And then call it before setRowTemplates: like so:
[self setVisibleMenuTitlesInPredicate:predicateTemplateA];

Using a String representing the name of a variable to set the variable

This is a basic example that I know can be simplified, but for testing sake, I would like to do this in such a way. I want to set a variable based on an appending string (the variables "cam0" and "pos1" are already declared in the class). The appending string would essentially be an index, and i would iterate through a loop to assign cameras (cam0, cam1..) to different positions (pos0, pos1..).
cam0 is defined as an UIImageView
pos1 is defined as a CGRect
This works for a NSString Variable named coverIndex:
NSString* text = [[NSString alloc] initWithString:#""];
NSLog(#"%#",(NSString *)[self performSelector:NSSelectorFromString([text stringByAppendingString:#"coverIndex"])]);
The correct string that I set for coverIndex was logged to the Console.
Now back to my UIImageView and CGRect. I want this to work.
NSString* camText = [[NSString alloc] initWithString:#"cam"];
NSString* posText = [[NSString alloc] initWithString:#"pos"];
[(UIImageView *)[self performSelector:NSSelectorFromString([camText stringByAppendingString:#"0"])] setFrame:(CGRect)[self performSelector:NSSelectorFromString([posText stringByAppendingString:#"1"])]];
My error is "Conversion to non-scalar type requested"
This is the only way I found to do this sort of thing (and get the NSLog to work), but I still believe there is an easier way.
Thank you so much for any help :)
Use KVC, it's an amazing piece of technology that will do just what you want:
for (int index = 0; index < LIMIT; index++) {
NSString *posName = [NSString stringWithFormat:#"pos%d", index];
CGRect pos = [[self valueForKey:posName] CGRectValue];
NSString *camName = [NSString stringWithFormat:#"cam%d", index];
UIImageView *cam = [self valueForKey:camName];
cam.frame = pos;
}
One way you can do this would be to create your cameras in a dictionary and use those special NSStrings to key in to it. Like,
NSMutableDictionary *myCams;
myCams = [[myCams alloc] init];
[myCams addObject:YOUR_CAM0_OBJECT_HERE forKey:#"cam[0]"];
[myCams addObject:YOUR_CAM1_OBJECT_HERE forKey:#"cam[1]"];
NSString camString = #"cam[0]"; // you'd build your string here like you do now
id theCamYouWant = [myCams objectForKey:camString];