Configuring NSPredicateEditor(RowTemplate) for Spotlight metadata queriesI' - cocoa

I'm trying to configure an NSPredicateEditor (in Interface Builder) to edit the predicate for an NSMetadataQuery.
As a first step, I'm trying to configure an NSPredicateEditorRowTemplate to accept key path(s) for the left-side expression, trying a single keyPath (kMDItemTextContent) to get started.
I can't figure out how to get all the pieces into IB. I've selected the row template, and set "Left Exprs" to "Key Paths" in the IB Attributes Inspector. But, using Apple's PhotoSearch example as a model, it appears that I should enter a user-readable attribute name (say, "Content") here; I can't figure out how to bind it to "kMDItemTextContent".
I've dissected the (correctly-configured) NIB in PhotoSearch(*), and inside it there is an NSKeyPathExpression specifying a metadata attribute attached to an NSPopUpButton/NSPopUpButtonCell.
I can't figure out where to navigate in IB to find the NSPopUpButton, and I'm not sure what I'd do to bind it to an NSExpression.
Any help appreciated.
(*) In case you're wondering, I got inside the NIB by converting it to a XIB, confirming that it still builds correctly, then examining it with BBEdit.

I've found that working with NSPredicateEditor and friends in Interface Builder is an exceedingly tedious task. For that reason, I do all of my row template configuration in code.
For your situation, it doesn't sound like you need a custom row template subclass, so you could probably just do:
#define NSPERT NSPredicateEditorRowTemplate
NSPERT * template = [[NSPERT alloc] initWithLeftExpressions:[NSArray arrayWithObject:[NSExpression expressionForKeyPath:kMDItemTextContent]]
rightExpressionAttributeType:NSStringAttributeType
modifier:NSDirectPredicateModifier
operators:[NSArray arrayWithObject:
[NSNumber numberWithUnsignedInteger:NSContainsPredicateOperatorType]]
options:(NSCaseInsensitivePredicateOption|NSDiacriticInsensitivePredicateOption)];
Once you've got the template, simply add it to the predicateEditor:
NSMutableArray * templates = [[myPredicateEditor rowTemplates] mutableCopy];
[templates addObject:template];
[template release];
[myPredicateEditor setRowTemplates:templates];
[templates release];
As for translating the "kMDItemTextContent", if it doesn't happen automatically (and I think it might), you could use the NSPredicateEditor localization options to display a different name.

Related

Is there a way to set referencing outlet in Interface Builder without Ctrl-Drag

So I'm just trying to create a very simple app for demo purposes here:
Created a Single View Application, using storyboards
Added a UIView to the storyboard
Added the following code to my controller's header file:
#property (weak, nonatomic) IBOutlet UIView *myView;
Now, I understand that I can link the UIView to the controller by:
arranging my code such that the header file is next to the storyboard
holding down Ctrl key and dragging it to the property in the header file
My question is this: can I do this without Ctrl-drag? And if so, how?
More specifically - it's annoying to have to put both my header file and storyboard on screen at the same time, and it seems there should be a way to make this connection without doing so.
I also understand that I can manually place the view by creating it inside my controller's viewDidLoad function, but I'd really like to use the interface builder to simplify / visualize things.
Edit: Is the answer to my question affected whether I use storyboards or xib/nib files? (I'd switch to use the one where it works)
you should be able to right click the element, and drag the "referencing outlet" item to the view's "File's Owner" in interface builder. There, it will give you a list of all available IBOutlets (matching the object's type).
In addition to Dima's answer, you can just as well use the Connection inspector in the Utilities pane

Where can I find .m file of UIKit

In EGOTableViewPullRefresh Demo I see he refer to property self.view and self.tableView in
EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.tableView.bounds.size.height, self.view.frame.size.width, self.tableView.bounds.size.height)];
From my understanding view and tableView is the same object, but tableView is a type cast of view, but when I want to see implementation file I can't find it.
I can see .h file with Option+click, but I can't find its implementation. How can I find .m file ?
You cannot see the TableViewController.m file because that implementation is private and Apple doesn't want you to be able to depend on an implementation--only the interface--for two reasons:
If you depend on a specific implementation, they'll never be able to change/improve it because they'll need to continue to provide backward compatibility for some time to come.
Apple doesn't want competitors (e.g., Android, etc.) to be able to easily copy their work.
You need to stick to the public documentation that Apple provides for the UITableView and friends. In there, I think you'll see that the view is an inherited property from the UIView class, whereas tableView is a property that connects the UITableViewController with the proper view for the table it is controlling.

Manual binding in Cocoa

I have an ImageView which shows a lock, informing if an opened file is locked or not. I have 2 images for locked and unlocked cases. I want synchronize the displayed image with boolean value of my object representing an opened file.
To do this I want my ViewController to change the image in my ImageView depending on lock state of object. So both object and ViewController have a property "isLocked".
How can I synchronize them? It is easy in IB but I don't know how to do it programmatically. I tried in initialize method of my ViewController to use:
[ViewController bind:#"value" toObject:[ArrayController selection] withKeyPath:#"isLocked" options:nil];
But it doesn't work. In documentation it is said that I have to expose my binding before using it.
I try to put the following code in initializer method of my object:
[self exposeBinding:#"isLocked"];
But Xcode doesn't recognize this method.
Does somebody have experience with this kind of bindings establishing?
As #nick says, you want Key-Value-Observing.
[arrayController addObserver:self
forKeyPath:#"selection.isLocked"
options:NSKeyValueObservingOptionNew
context:#"this_context"]
Then when isLocked changes the -observeValueForKeyPath:ofObject:change:context: method that you have added to your viewController will be called (as long as you only manipulate isLocked in a KVC compliant way).
The options parameter lets you optionally tweak exactly what conditions will trigger the notification and what data is sent along with the notification. The context parameter is there to help you distinguish between notifications that you registered to receive and notifications your superclass registered to receive. It is optional.
Bindings seem like they might be useful to keep two values in sync. However, this is not what they do at all.
Yes, lots of things seem to give the impression that this is what they do, and there isn't much saying that this isn't what they do, also lots of people believe that this is what they do - but no, you cannot use them for this.
Only a handful of classes support bindings (they are listed here) and then, and this is the important bit, those classes only support binding their named bindings, and these bindings are not instance variables. eg NSTextField has a 'fontFamilyName' binding yet NSTextField does not have a 'fontFamilyName' property or instance variable, even a derived one. NSTextField does have a 'isBordered' property but not a binding - so you cannot bind 'isBordered'.
It does not mean anything to 'bind' an arbitrary property of an arbitrary Class.
Yes, you can bind two arbitrary values, the following code works just fine:
#import <Cocoa/Cocoa.h>
#interface SomeObject : NSObject
#property (retain,nonatomic) id someValue;
#end
#implementation SomeObject
#end
int main()
{
SomeObject *source=[SomeObject new];
SomeObject *target=[SomeObject new];
[target bind:#"someValue" toObject:source withKeyPath:#"someValue" options:0];
[source bind:#"someValue" toObject:target withKeyPath:#"someValue" options:0];
[source setSomeValue:#(42)];
NSLog(#"target: %#",[target someValue]);
[target setSomeValue:#(22)];
NSLog(#"source: %#",[source someValue]);
return 0;
}
As far as I can tell, the problem is the bit [ArrayController selection]. The first problem is that ArrayController is (or should be) a class, and getting the class's selection is probably pointless. The other problem is that even if this were an instance, you would be binding to the selection at the time of the call, which is almost certainly not what you want. You want to track the current selection as it changes.
So what you want is probably something like the following:
[myViewController bind:#"value" toObject:myArrayController withKeyPath:#"selection.isLocked" options:nil];

How do I add custom buttons to UIAlertView?

How do I add custom buttons to UIAlertView ??
UIButton *newButton=[[UIButton alloc]init];
[self.alertview addSubview:newButton];
NOTE: dont forget to specify the frame to ur button.
If you only want to customize the button's names, then you can use the "otherButtonTitles" section of the following initializer:
– initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:
(You can also add buttons with a custom name using the - (NSInteger)addButtonWithTitle:(NSString *)title instance method).
However, if you want to customize more than just the title of the button, then you need to add a subview of your custom button, as show in madhu's sample code.
You will also need to set the frame of your newButton object so as it's positioned where you want it inside the UIAlertView instance. Because the UIAlertView isn't particularly designed for nesting subviews within it (this is a common thing for developers to want to do and struggle with), you may encounter some strange effects that requires fiddling around with your view hierarchy in order to solve. I'm sure there are plenty questions out there that describe some of these strange effects, and I myself have asked about at least one of them!

Cannot bind NSSlider in IB?

I just created a new Xcode project. In the AppControl class Header file I have the following objects defined (and some other ones, too):
IBOutlet NSImageView *inputImageView;
IBOutlet NSImageView *outputImageView;
IBOutlet NSTextField *myNoiseLevel;
IBOutlet CGFloat *mySharpness;
After putting the basic code into the .h and .m files, I then went into Interface Builder and created my UI. I was able to bind the two NSImageView controls in IB to the corresponding NSImageView objects listed above. And I was able to bind a couple of other objects/controls, also. But I am NOT able to bind the last two items listed (myNoiseLevel and mySharpness) to the NSSlider controls I have on the application main window. I'm not sure why. I know this kind of thing is probably hard to diagnose, because it is not "strictly code related," but if there is something "tricky" about binding sliders please let me know what the main "suspects" are that I should check.
This is my first attempt to use a slider control through IB. I have a book (Cocoa programming for Mac OS X, 3rd ed., by A. Hillegass) that I am using to learn about the basic way to do this stuff. And he has a slider example in there. But his slider example is "continuous" and it uses key path binding. I think this is overkill for what I want/need to do -- I just want to pull the value from the slider when another button is pushed (no need for "continuous" update). So I am trying to directly bind the "outlets" listed when I right-click on my App Control object (one for each of those items shown above), to the slider controls on my window. But when I cntl-drag from the AppControl outlet up to the corresponding slider, the slider will not "accept" the arrow I'm dragging.
Does this make sense? Any idea what I'm doing wrong and/or what I need to do to make the binding work? I have tried saving / building / closing & reopening IB and Xcode -- all to make sure IB has the latest version of everything. Still no luck, though.
One last thing ... What I really need are CGFloat numbers, from the slider. Can I simply declare the Outlet as CGFloat type ... or do I need to define it as NSTextField (or something else), and then convert it to Float in my program? You can see in the IBOutlets I pasted above, that I was trying different data types for the outlets (trying to see if my defining them as CGFloat was somehow preventing the bindings).
Make the outlet an NSSlider*. You should then be able to connect to it. When you need the value (eg, in response to the button press you mention) call [yourSliderOutletName doubleValue].
More generally: an IBOutlet is an ivar that can be filled in with a pointer to the actual object awoken from a NIB file. As such, it needs to be of an appropriate type to hold that pointer -- the object's actual class, or one of its superclasses or protocols, or (least informatively) id. You can't just arbitrarily connect an object to any old variable, like your CGFloat. There's no implicit conversion -- how is the system supposed to know what you want?

Resources