I have the following delegate method which is listening for when text changes in an editable field:
-(void)controlTextDidEndEditing:(NSNotification *)aNotification{
Say, I have two NSTextFields declared, how can I find out which is the NSTextField that generated the notification? I want to perform some code when one text field's text did finish editing and other code when the other text field's code finishes editing.
In other words, how can I get the name of the text field that gave the notification?
Thank you!
[aNotification object] is the NSControl (or NSControl subclass) object which posted the notification.
You could use tags and get the tag field of the object, but first you have to force downcast the object from Any to NSTextField so you can access the tag property (an object of "any" doesn't have a "tag" property) like so:
let object = aNotification.object as! NSTextField
then you can check the tag:
if object.tag == 99 { do something }
That's how I addressed the issue in some of the code I'm working on now.
Related
I'm writing my first master detail view in Cocoa. My data model is really simple: just an NSMutableArray that contains instances of a single class having a few NSStrings properties.
Here's what I've created so far:
A class representing the instances.
An NSMutableArray specified as a property of my app delegate to hold the class instances.
A master detail view, with an NSTable and some text fields to hold the properties of a selection instance.
An NSArrayController with a binding specifying the app delegate, and the name of the NSMutableArray property as the model key path.
Bindings between the columns of the NSTable and the NSArrayController (controller key = "arrangedObjects", and model key path of each column = a property of the class).
Bindings between the text fields of the view and the selection (controller key = "selection", and model key path of each text field = a property of the class).
A "+" button and a "-" button in the view to enable adding and removing objects.
However, I'm having two problems with this design:
(1) I can't find a good way to implement the "+" and "-" buttons. First, I bound them to the add: and remove: properties of the array controller. While this seems to work, it has a problem: my class declares an init member that initializes the NSStrings to stub values, but the array controller doesn't seem to [init] the new instances, because the new list entry has empty strings for each column.
Next, I attached them to IBActions in my app delegate that added or removed an object from the NSMutableArray. However, this feels wrong - it feels like I'm violating the model-view-controller architecture by not talking to the array controller. For example, the "-" function has to talk to the array controller to get the selected item. Also, I notice that I have to send a [didChangeValueForKey] message after altering the array - which feels like a signal that I'm doing this wrong.
(2) One of the detail subviews in my view is an NSTextView that's bound to an NSString in the selected object. It really isn't working as expected: when new text is entered, the text remains the same even if other members of the class (which should have different values) are selected. Also, the text data isn't being saved to any instance of the class.
Any thoughts? Thanks in advance.
Here is an example that should be close to what you want:
#import "AppDelegate.h"
#import "Members.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.theData = [NSMutableArray arrayWithObject:[[Members alloc] init]];
}
-(IBAction)addMember:(id)sender {
[self.controller addObject:[[Members alloc] init]];
}
-(IBAction)removeMember:(id)sender {
[self.controller removeObjectAtArrangedObjectIndex:self.controller.selectionIndex];
}
The array controller (whose IBOutlet is controller) has its content array bound to theData. Members is the name of my class that has 3 string properties, name, author and blurb. In the detail view, the 2 text field's are bound to ArrayController.selection.name or author, just like you said you did. The other property, blurb, is bound the same way (ArrayController.selection.blurb), but to the Attributed String value of a text view. This worked fine, I'm not sure what your trouble with the text view was. If I add text to the text view, it does show up there if I click another row in the mater table and then click back (also if I log the array, it shows up there too).
I'm making a little demo app and I'm having trouble changing.
Heres the thing:
I have a UIButton that every click will add a character in a NSString in a UITextField.
And I put an IBAction(Mudar_Resposta) on that UITextField(campo) in the part 'Value Changed'.
In that IBAction, I put that:
- (IBAction)MudarResposta:(id)sender {
campo.text=#"lol";
}
But I can't get it to work. Any ideas?
Thanks.
The proper way to get it to work is to connect an action method to the Touch Up Inside event for your button. Within that method, edit they text for your text field.
- (IBAction)mudarResponsta:(id)sender { // Connect to Touch Up Inside of your button
campo.text = [NSString stringWithFormat:%# %#", campo.text, #"lol"]; // Add string ' lol' every time it's called
}
Connecting to Value Changed will never invoke the method because the value is never being changed—you're connecting the changing method to the change…if that makes any sense.
Most likely you didn't connect that action to anything in the XIB file.
- (IBAction)Button:(id)sender {
[self.contacts addObject:_txtField.text];
_txtField.text=#"";
}
Here contacts is an Array name which is a Mutable Array. txtField is UITextField.
I've already killed a day on this subject and still got no idea on how could this be done in a correct way.
I'm using NSOutlineView to display filesystem hierarchy. For each row in the first column I need to display checkbox, associated icon and name of the file or directory. Since there's no standard way to make this, I've subclassed NSTextFieldCell using both SourceView and PhotoSearch examples, binding value in IB to name property of my tree item class though NSTreeController. I'm using drawWithFrame:inView: override to paint checkbox and image, forwarding text drawing to super. I'm also using trackMouse:inRect:ofView:untilMouseUp: override to handle checkbox interaction.
Everything was fine up until I noticed that once I press mouse button down inside my custom cell, cell object is being copied with copyWithZone: and this temporary object is then being sent a trackMouse:inRect:ofView:untilMouseUp: message, making it impossible to modify check state of the original cell residing in the view.
Since the question subject is about binding, I thought this might be the answer, but I totally don't get how should I connect all this mess to function as expected. Tried this:
[[[treeView outlineTableColumn] dataCell] bind:#"state"
toObject:treeController
withKeyPath:#"selection.state"
options:nil];
but didn't succeed at all. Seems like I'm not getting it.
May this be a completely wrong way I've taken? Could you suggest a better alternative or any links for further reading?
UPD 1/21/11: I've also tried this:
[[[treeView outlineTableColumn] dataCell] bind:#"state"
toObject:treeController
withKeyPath:#"arrangedObjects.state"
options:nil];
but kept getting errors like "[<_NSControllerTreeProxy 0x...> valueForUndefinedKey:]: this class is not key value coding-compliant for the key state." and similar.
You bind a table (or outline) column's value, not an individual data cell's state. The data cell's object value is set to the current row/col's value then drawn so you don't have potentially thousands (or millions?) of cells created for no good reason.
Further, you want the tree or array controller's arrangedObjects, not its selection.
Bind the column's value to the tree controller's arrangedObjects as the controller key, and "state" as the model key path in IB; or #"arrangedObjects.state" in code as above.
Okay, I've managed to do what I needed by binding columns's value to arrangedObject's self (in IB) and overriding cell's setObjectValue: so that it looks like:
- (void) setObjectValue:(id)value
{
if ([value isMemberOfClass:[MyNodeClass class]])
{
[super setObjectValue:[value name]];
[self setIcon:[value icon]];
[self setState:[value state]];
}
else
{
if (!value)
{
[self setIcon:nil];
[self setState:NSOffState];
}
[super setObjectValue:value];
}
}
Actual state change is performed within another class, connecting its method to cell's selector (in IB) which I call using
[NSApp sendAction:[self action] to:[self target] from:[self controlView]];
from cell's trackMouse:inRect:ofView:untilMouseUp:. This another class'es method looks like this:
- (IBAction) itemChecked:(id)sender
{
MyNodeClass* node = [[sender itemAtRow:[sender clickedRow]] representedObject];
if (node)
{
[node setState:[node state] == NSOnState ? NSOffState : NSOnState];
}
}
I am trying to learn cocoa and have a few problems with KVC and bindings. I have a nstableview with three columns; "checkbox", "text", "icon". The values of each column is binded to an arraycontroller using KVC. When program is launched the rows and columns are correctly filled into the tableview according to the values in the array. I can click a row and correctly print the content of that row using something like this:
- (IBAction)fileTableViewSelected:(id)sender{
NSInteger r;
NSDate *fModOne;
id object;
r = [[NSNumber numberWithInt:[sender selectedRow]] intValue];
object = [arrayIntersect objectAtIndex:r];
fModOne = [object valueForKey:#"fileModifiedDirOne"];
NSLog(#"Date found in row is %#",fModOne);
}
My problem is when I try to click the checkbox in column one and change the value of the box. Initially, the value of the checkbox is set to 1 using the arraycontroller which works fine, but when I want to change the value of the checkbox of a specific row to 0 by clicking on it the program crashes. When the box is clicked an action is correctly called and this is where I thought I could simply change the value of my objects BOOL by calling:
[object setValue:[NSNumber numberWithBool:NO] forKey:#"doSync"];
My setters and getters for the BOOL doSync is defined as:
#property(nonatomic, readwrite) BOOL doSync;
#dynamic doSync;
- (void)setDoSync:(BOOL) value{
NSLog(#"setting dosync %i", value);
doSync = NO;
}
- (BOOL)doSync{
return doSync;
}
I have searched everywhere for a solution to my problem, but I am unable to find any examples of how to use checkboxes in tableview using KVC and bindings. I appreciate any help I can get on this and I would appreciate any examples I could take a look at.
Cheers and thanks! Trond
You don't need to implement this yourself as an action. Just bind the column through your array controller's arrangedObjects to the doSync property of the model objects.
If you don't want to use Bindings, you still shouldn't implement it as an action. Instead, be the table view's data source and respond to the message the table view will send you to change one of the values.
#dynamic doSync;
There's no reason to have this if you turn around and implement the accessors for that property in the same class.
If this is a managed-object class and the property is an attribute of the entity, then your accessors should send [self willAccessValueforKey:] before and [self didAccessValueForKey:] after accessing the instance variable. If that's all they do, then you should not implement the custom accessors at all; cut them out and have #dynamic alone.
- (void)setDoSync:(BOOL) value{
doSync = NO;
That's not setting the property to the value passed in.
I have an NSSlider bound to a text field to display the value given by the slider.
Everything works, but I would like the NSSlider to send "nil" as minValue to the object.
For example, I can convert it to nil when saving (to a propertylist):
if ([myOb intValue] > 0)
[myDict setObject:... forKey:...]
But I would like to apply same behavior when the app is running, cause some other fields are enabled (with binding) only if the value of "myObj" is nil. The problem is the NSSlider always returns "0", and minValue=nil is not accepted by NSSlider.
Thanks in advance,
Ronan.
It won't accept nil as the minValue. The reason, if you look at the NSSlider documentation, is because minValue and maxValue are both defined as doubles, which are not object types.
If you want to customize the value that is displayed in the text field, you would probably want to create an NSValueTransformer subclass to convert double values to NSString values for the text field. Read the Value Transformer Programming Guide for details on how to do this. I have done this in the past so that I can have a label that displays time values similar to the screen saver slider in System Preferences. This works quite well.
Finally i solved my problem, by associate the slider to an IBAction:
- (IBAction)slideValue:(id)sender {
if ([mySlide intValue]==0) { //slider
[myNumBox setStringValue:#""]; //textfield
[myValue setValue:nil]; // value
}
}
So, when the slider is equal to O, it return "" in the textfield (nil don't works).