Setting the outlets of objects
Dragging objects from the object library into the interface builder,
buttons, text fields, tables whatever you require we can rapidly
construct a UI. To connect one object to another setting its outlets,
^ drag this time, from the object that needs to know to the
object that it needs to know about.
Labels
Labels can be set to NSTextField objects, giving your program the ability to alter their content. Right clicking upon the object that contains the NSTextField and then select the field from the outlets and drag over to the Label object in the interface display.
Text Field
Text fields like labels are backed by NSTextField objects, the text filed connection is slightly different, the text filed will accept user input to set its content and is read from within the program so as to input data, where the label was solely outputting.
Buttons
We need to set both the target and the action of buttons. Right click upon the object that contains the button action, in the left side panel of the interface builder, select that action and drag over to the button in the display to set.
Else drag from a button into the code of a class within the assistant window, either drop onto an existing action or into a life of the file, this will open up the dialogue box to create the required action.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
@interface RandomController : NSObject
{
IBOutlet NSTextField *textField;
}
- (IBAction)generate:(id)sender;
- (IBAction)seed:(id)sender;
@end
awakeFromNib
Objects can override the awakeFromNib function in order to run code before runtime.
- (void)awakeFromNib
{
NSDate *now;
now = [NSDate date];
[textField setObjectValue:now];
}
NSTableView
dataSource
The NSTableView is used for displaying tables, it requires a dataSource object, this object is expected to have some specific methods; We say that, the data source must conform to the NSTableDataSource's informal protocol. For which it must implement, at the very least, the two following methods.
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex;
- (void)tableView:(NSTableView *)aTableView
setObjectValue:(id)anObject
forTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex;
// for editable cells...
- (void)tableView:(NSTableView *)aTableView
setObjectValue:(id)anObject
forTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex;
First, you will set the dataSource outlet of the NSTableView to be the desired delegate, the class that implements the require methods. Select the NSTableView. Control-click in the table view to bring up the Connections panel, then drag from the dataSource outlet to the applications desired delegate class for the dataSource.
If you do not see dataSource in the inspector, you have selected NSScrollView, not NSTableView inside it. just click in the interior of the table view until the title of the Connection panel says NSTableView.
Select the table view, then in the inspector panel on the right hand side of the interface builder, set the table cell type and the desired number of column's, cell based is much simpler than view based.
Now create a tableView outlet inside of the delegate class, to connect the table view outlet to. For example: Make sure that you have the table view selected and Control-drag to the class declaration in the header file.
We need also to set the inner table view object to have what will usually be the same delegate as the entire object.
Key Values Coding and Key Value Observing
The NSObject type from which all Objective-C class inherit, lends to
its inheritors two functions for acting upon the classes internal
members, for example: given a custom made class with a member
name setName can be called to set the members
value and name to retrieve it.
By declaring these method in our class we can override them in order to inject functionality..
Sliders
The somewhat cumbersome NSInteger type that is required to have our get and set methods becomes more obviously useful when we see how these methods are used by an object such as the NSSlider as it calls the set and the get methods to both know its initial position and to update it whenever it is moved.
If the backing value for the slider is changed elsewhere in a program, then the sliders backing type must also be updated. We can do this by explicitly trigger the notification of the observers.
- (IBAction)incrementSlider:(id)sender
{
[self willChangeValueForKey:@"value"];
value++;
NSLog(@"value is now %d", value);
[self didChangeValueForKey:@"value"];
}
Or we can use key-value coding
- (IBAction)incrementSlider:(id)sender
{
NSNumber *n = [self valueForKey:@"value"];
NSNumber *npp = [NSNumber numberWithInt:[n intValue] + 1];
[self setValue:npp forKey:@"value"];
}
Or the accessor method
- (IBAction)incrementSlider:(id)sender
{
[self setValue:[self value] + 1];
}
Rather than declaring set and get methods manually, the objective-c language will make them for us automatically when we create a variable as a property rather than just an instance.
@interface KvcFunAppDelegate : NSObject
{
int value;
}
@property (assign) IBOutlet NSWindow *window;
@property (readwrite, assign) int value;
- (IBAction)incrementValue:(id)sender;
@end
Attributes of a Property
readwrite or readonly, the default is
readwrite, a property marked with readonly gets
no get or set methods. To describe how the set method works a property
can include one of the following attributes:
assign
The default, this makes a simple assignment happen, commonly used
for scalar non pointer types such as integers and floating point
values.
strong
This keeps the object that is being pointed to from being
deallocated whilst this point is still set. Specific to ARC code, if
not using ARC then retain is the equivalent.
weak
Denotes a weak reference, similar to assign, except that once the
object being pointed to is deallocated, the property is set to nil.
copy
Makes a copy of the new value and assigns the variable to that copy.
This attribute is often used for properties that are strings and other
classes with mutable sub classes.
If your program is multithreaded your attributes might also include
nonatomic or atomic. By default the
@synthesize will generate accessors with this attribute. If
you know that your property does not need to be atomic, you can assign to
it the nonatomic attribute and negate the overhead.
If a property matches a incidence variable you can use
@synthesize, xcode will by default use an underscore at the
start of the underlying variables name _value.
@synthesize value;
If you would like to add your own prefix to the instance variable, you can do so like this:
@synthesize value = _my_value;
Key Paths
Objects that are related can be accessed using key paths, this is
effectively the . notion used within the
@"key.path.accessor.syntax" There are also operators that
can be applied within the request. average = [this
valueForKeyPath:@"@avg.some.value"];
- @avg
- @count
- @max
- @min
- @sum
bindings
Having learned about key paths we can now take a look at bindings.
[textField bind:@"value" toObject:employeeController
withKeyPath:@"arrangeObjects.@avg.expectedRaise"
options:nil];
Bindings can also be created automatically by making connections in the interface builder. Create a document based project and then within the document.xib, add an NSArrayController and set its class, then add to the controller any specific key values that are required, such as the names of required attributes from the underlying class. Next open the array controllers bindings inspector and find the content array binding and expand it. Check 'bind to' then 'File's Owner', leave the controller key empty but set the Model Key Path to the desired array instance.