NSTextView delegate not working? - cocoa

I have a NSTextView where i put the delegate to my file owner. My file owner is then assosiated with a class where i do different stuff in the view.
To my understanding it should now be possible to catch events from the NSTextView inside my class (because i have set its delegate to file owner), but it does not seem to work, why is that?
I have implemented this function in my class:
- (BOOL)control: (NSControl *)control textView:(NSTextView *)textView doCommandBySelector: (SEL)commandSelector {
NSLog(#"i was fired!");
return YES;
}

according to http://developer.apple.com/library/mac/#documentation/cocoa/Reference/NSTextViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intf/NSTextViewDelegate
the method signature looks like:
- (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
give that a try instead.

The selector is just textView:doCommandBySelector:; drop the control.

Related

NSTextField and NSTextView : Overlapping Delegate Methods

I have a class which was the delegate for NSTextViews, for which I was interested in the textDidEndEditing: method. I now want it to also be the delegate for NSTextFields. Problem is, they both use the same method for signaling end of text editing.
I tried to "fork" my textDidEndEditing: method to deal with both NSNotifications, but it seems like the latest (NSTextFields) don't trigger any message.
Should I be looking for an inside bug, or it is a known limitation ?
- (void)textDidEndEditing:(NSNotification *)aNotification
{ if ([[aNotification object] isKindOfClass:[NSTextView class]])
{
}
else if ([[aNotification object] isKindOfClass:[NSTextField class]])
{
}
}
Change your method and try below:-
- (void)controlTextDidEndEditing:(NSNotification *)aNotification

Handle textDidChange Method in NSTextView

I want trigger the event if the text is changing in my NSTextView. Is there a way to trigger that directly from the NSTextView or I have to create a custom Class for the NSTextView?
- (void)textDidChange:(NSNotification *)notification
{
----do stuff here----
NSLog(#"text typed");
}
add the method to your code...enough

NSTextfield complete

It is there a solution to have a completion of a NSTextField with method :
- (NSArray *)control:(NSControl *)control textView:(NSTextView *)textView completions:(NSArray *)words forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int*)index
with several words and not one? Because when you type a space, the completion start again...
Thanks.
Better late than never, it might be helpful for others:
It's a little tricky problem, since NSControlTextEditingDelegate / NSTextFieldDelegate doesn't offer a way to solve it directly. What you need to do is creating a custom subclass of NSTextView (yes, text view), and override the method - (NSRange)rangeForUserCompletion:
- (NSRange)rangeForUserCompletion
{
return [self selectedRange];
}
And then subclass NSTextFieldCell to override the method - (NSTextView *)fieldEditorForView::
- (NSTextView *)fieldEditorForView:(NSView *)aControlView
{
static MyTextView* _myFieldEditor = nil;
if (_myFieldEditor == nil) {
_myFieldEditor = [[MyTextView alloc] init];
[_myFieldEditor setFieldEditor:YES];
}
return _myFieldEditor;
}
Then in Interface Builder, set the class of your text field's cell to your subclass of NSTextFieldCell. What will happen is when your text field becomes first responder, the window will call your cell's -fieldEditorForView: method, and use your custom text view as the field editor. So during editing the value of your text field, any completion will call -(NSRange)rangeForUserCompletion on your text view.
Then you can fine tune your -rangeForUserCompletion to make it return the exact range you want for the completion.
Also, the code in fieldEditorForView: assumes that your app uses only one window, if you are using multiple windows (e.g. document-based apps), you'll have to change it and keep one field editor instance per window.
Hope it helps :)

How does an NSView subclass communicate with the controller?

I am brand spanking new to Cocoa programming, and am still kind of confused about how things wire together.
I need a pretty simple application that will fire off a single command (let's call it DoStuff) whenever any point on the window is clicked. After a bit of research it looks like subclassing NSView is the right way to go. My ClickerView.m file has this:
- (void)mouseDown:(NSEvent *)theEvent {
NSLog(#"mouse down");
}
And I have added the View to the Window and have it stretching across the whole thing, and is properly writing to the log every time the window is clicked.
I also have my doStuff method on my controller (this could be refactored to its own class I suppose, but for now it works):
- (IBAction)doStuff:(id)sender {
// do stuff here
}
So, how do I get mouseDown in ClickerView to be able to call DoStuff in the controller? I have a strong .NET background and with that, I'd just have a custom event in the ClickerView that the Controller would consume; I just don't know how to do that in Cocoa.
edit based on Joshua Nozzi's advice
I added an IBOutlet to my View (and changed it to subclass NSControl):
#interface ClickerView : NSControl {
IBOutlet BoothController *controller;
}
#end
I wired my controller to it by clicking and dragging from the controller item in the Outlets panel on the View to the controller. My mouseDown method now looks like:
- (void)mouseDown:(NSEvent *)theEvent {
NSLog(#"mouse down");
[controller start:self];
}
But the controller isn't instantiated, the debugger lists it as 0x0, and the message isn't sent.
You could either add it as an IBOutlet like Joshua said, or you could use the delegate pattern.
You would create a Protocol that describes your delegate's methods like
#protocol MyViewDelegate
- (void)doStuff:(NSEvent *)event;
#end
then you'd make your view controller conform to the MyViewDelegate protocol
#interface MyViewController: NSViewController <MyViewDelegate> {
// your other ivars etc would go here
}
#end
Then you need to provide the implementation of the doStuff: in the implementation of MyViewController:
- (void)doStuff:(NSEvent *)event
{
NSLog(#"Do stuff delegate was called");
}
then in your view you'd add a weak property for the delegate. The delegate should be weak, so that a retain loop doesn't form.
#interface MyView: NSView
#property (readwrite, weak) id<MyViewDelegate> delegate;
#end
and then in your view you'd have something like this
- (void)mouseDown:(NSEvent *)event
{
// Do whatever you need to do
// Check that the delegate has been set, and this it implements the doStuff: message
if (delegate && [delegate respondsToSelector:#selector(doStuff:)]) {
[delegate doStuff:event];
}
}
and finally :) whenever your view controller creates the view, you need to set the delegate
...
MyView *view = [viewController view];
[view setDelegate:viewController];
...
Now whenever your view is clicked, the delegate in your view controller should be called.
First, your view needs a reference to the controller. This can be a simple iVar set at runtime or an outlet (designated by IBOutlet) connected at design time.
Second, NSControl is a subclass of NSView, which provides the target/action mechanism machinery for free. Use that for target/action style controls. This provides a simple way of setting the reference to your controller (the target) and the method to call when fired (the action). Even if you don't use a cell, you can still use target/action easily (NSControl usually just forwards this stuff along to its instance of an NSCell subclass but doesn't have to).
you can also use a selector calling method,
define two properties in custom class:
#property id parent;
#property SEL selector;
set them in view controller:
graph.selector=#selector(onCalcRate:);
graph.parent=self;
and call as:
-(void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
[_parent performSelector:_selector withObject:self];
}

Disable/Enable NSButton if NSTextfield is empty or not

I´m newbie with cocoa. I have a button and a textField in my app. I want the button disabled when the textfield is empty and enabled when the user type something.
Any point to start? Any "magic" binding in Interface Builder?
Thanks
[EDITED]
I´ve tried to set the appDelegate as the NSTextfield´s delegate and added this method (myTextfield and myButton are IBOutlets):
- (void)textDidChange:(NSNotification *)aNotification
{
if ([[myTextField stringValue]length]>0) {
[myButton setEnabled: YES];
}
else {
[myButton setEnabled: NO];
}
}
But nothing happens...
I´ve tried to set the appDelegate as the NSTextfield´s delegate and added this method (myTextfield and myButton are IBOutlets):
- (void)textDidChange:(NSNotification *)aNotification
{
if ([[myTextField stringValue]length]>0) {
[myButton setEnabled: YES];
}
else {
[myButton setEnabled: NO];
}
}
That's the hard way, but it should work just fine. Either you haven't hooked up the text field's delegate outlet to this object, you haven't hooked up the myTextField outlet to the text field, or you haven't hooked up the myButton outlet to the button.
The other way would be to give the controller a property exposing the string value, bind the text field's value binding to this stringValue property, and bind the button's enabled binding to the controller's stringValue.length.
You could also give the controller two properties, one having a Boolean value, and set that one up as dependent upon the string property, and bind the button to that. That's a cleaner and possibly more robust solution, though it is more work.
Here's a solution using bindings.
Below I setup a NSTextField that is bound to the file owner's "text" property. "text" is a NSString. I was caught by "Continuously Updates Value". Thinking my solution didn't work but really it wasn't updating as the user typed, and only when the textfield lost focus.
And now setting up bindings on the button, simply set its enabled state to the length of the file owner's text property.
Annd, the working product.
If you use controlTextDidChange instead of textDidChange, you can get rid of the notification stuff and just rely on being the NSTextField's delegate.
Thanks Peter. What I missed (in my hard way version) is this piece of code in the awakeFromNib in the appDelegate:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:#selector(textDidChange:) name:NSControlTextDidChangeNotification object:myTextField];
It works perfect. Now I´m trying the easy way, but I´m afraid I´m not still good enough with the bindings.
To bind the property
#property (retain) IBOutlet NSString *aStringValue;
with the textfield´s value, what I have to use in IB for "Bind to:", "Controller Key" and "Model Key Path"?

Resources