Move focus to newly added record in an NSTableView - objective-c

I am writing an application using Core Data to control a few NSTableViews. I have an add button that makes a new a record in the NSTableView. How do I make the focus move to the new record when this button is clicked so that I can immediately type its name? This is the same idea in iTunes where immediately after clicking the add playlist button the keyboard focus is moved to the new line so you can type the playlist's name.

Okay well first of all, if you haven't already got one, you need to create a controller class for your application. Add an outlet for the NSArrayController that your objects are stored in, and an outlet for the NSTableView that displays your objects, in the interface of your controller class.
IBOutlet NSArrayController *arrayController;
IBOutlet NSTableView *tableView;
Connect these outlets to the NSArrayController and the NSTableView in IB. Then you need to create an IBAction method that is called when your "Add" button is pressed; call it addButtonPressed: or something similar, declaring it in your controller class interface:
- (IBAction)addButtonPressed:(id)sender;
and also making it the target of your "Add" button in IB.
Now you need to implement this action in your controller class's implementation; this code assumes that the objects you have added to your array controller are NSStrings; if they are not, then replace the type of the new variable to whatever object type you are adding.
//Code is an adaptation of an excerpt from "Cocoa Programming for
//Mac OS X" by Aaron Hillegass
- (IBAction)addButtonPressed:(id)sender
{
//Try to end any editing that is taking place in the table view
NSWindow *w = [tableView window];
BOOL endEdit = [w makeFirstResponder:w];
if(!endEdit)
return;
//Create a new object to add to your NSTableView; replace NSString with
//whatever type the objects in your array controller are
NSString *new = [arrayController newObject];
//Add the object to your array controller
[arrayController addObject:new];
[new release];
//Rearrange the objects if there is a sort on any of the columns
[arrayController rearrangeObjects];
//Retrieve an array of the objects in your array controller and calculate
//which row your new object is in
NSArray *array = [arrayController arrangedObjects];
NSUInteger row = [array indexOfObjectIdenticalTo:new];
//Begin editing of the cell containing the new object
[tableView editColumn:0 row:row withEvent:nil select:YES];
}
This will then be called when you click the "Add" button, and the cell in the first column of the new row will start to be edited.

I believe an easier and more proper way to do it is by implementing it this way.
-(void)tableViewSelectionDidChange:(NSNotification *)notification {
NSLog(#"%s",__PRETTY_FUNCTION__);
NSTableView *tableView = [notification object];
NSInteger selectedRowIndex = [tableView selectedRow];
NSLog(#"%ld selected row", selectedRowIndex);
[tableView editColumn:0 row:selectedRowIndex withEvent:nil select:YES];
I.e.
Implement tableViewSelectionDidChange:(NSNotification *)notification
fetch the selected row index
Call editColumn:(NSInteger)column row:(NSInteger)row withEvent:(NSEvent *)theEvent select:(BOOL)select from there with the row index.
Important note: this solution will also trigger the editing when user will simply select a row. If you only want editing triggered when adding a new object, this is not for you.

Just create a separate #IBAction in your controller and invoke the NSArrayController.add method manually. After that you can select the row
#IBAction func addLink(_ sender: Any) {
// Get the current row count from your data source
let row = links.count
arrayController.add(sender)
DispatchQueue.main.async {
self.tableView.editColumn(0, row: row, with: nil, select: true)
}
}

Related

Get NSTextField value from NSWindow beginSheet completion handler

I created a window that slides down from the top using the beginSheet method, on the window is some NSTextFields where a user can enter data and then when the user presses the ok button to provided the completion handler NSModalResponsOK, how can I get the values from the NSTextField in the NSWindow Sheet action to the view that called the beginSheet method. I created an initMethod that passes in the current View controller object to the NSWindow sheet modal so that I could try to set the parent view controller data properties. I can get the data showing in the completion handler block, but cannot get the values out of the completion handler block.
here is the action to show the sheet
- (IBAction)addJobDescription:(id)sender {
NSLog(#"fired add job desc");
self.addJobDescriptionViewController = [[AddJobDescriptionViewController alloc]initWithWindowNibName:#"AddJobDescriptionViewController" andWithViewController:self];
[[[self view] window] beginSheet:self.addJobDescriptionViewController.window completionHandler:^(NSModalResponse returnCode){
NSLog(#"%ld",(long)returnCode);
if (returnCode == 1) {
[self setJobDescriptionTableDataWithNumber:self.jobDescriptionNumberArray andWithDescription:self.jobDescriptionArray andTotal:self.JobDescriptionTotalArray];
NSLog(#" first job description: %#",self.jobDescriptionArray[0]);
}
}];
}
Here is the dismiss action from the NSwindow instance that has the sheet view and NSTextFields
- (IBAction)endSheetView:(id)sender {
self.nEstimateViewController.jobDescriptionArray = [[NSArray alloc]initWithObjects:self.jobDescriptionOne.stringValue,self.jobDescriptionTwo.stringValue,self.jobDescriptionThree.stringValue,self.jobDescriptionFour.stringValue, nil];
self.nEstimateViewController.JobDescriptionTotalArray = [[NSArray alloc]initWithObjects:self.descriptionTotalOne.stringValue,self.descriptionTotalTwo.stringValue,self.descriptionTotalThree.stringValue,self.descriptionTotalFour.stringValue, nil];
self.nEstimateViewController.jobDescriptionNumberArray = [[NSArray alloc]initWithObjects:#"1",#"2",#"3",#"4", nil];
[self.window.sheetParent endSheet:self.window returnCode:NSModalResponseOK];
}
I have tried to make a method to call from the completion block but no dice. I want to get the data from the NSTextFields on the NSWindow Sheet view to add to an array to update a NSTableView. I am going crazy trying everything but I havent been able to find a solution. I am thinking I need the __block some where or make the parent view controller a delegate of the NSWindow sheet instance.
Let me know if you need any other info or code.

Get row of NSPopUpButton in view based NSTableView

I have a view based table view with one row containing NSPopupButtons. When the user changes the popUpButton I need to get get the table row in which this popUpButton is contained.
I first expected to get the row with
NSInteger clickedRow = [tableView rowForView: ((NSPopUpButton*) sender)];
But the sender of the action always is a NSMenuItem object not the NSPopUpButton. NSMenuItem however is not a view so I cannot use rowForView with that.
Currently my IBAction looks like this:
- (IBAction)changedPopUp:(id)sender {
NSMenuItem* selectedMenuItem = ((NSMenuItem*) sender);
NSPopUpButton* popupButton = (NSPopUpButton*)[selectedMenuItem view];
NSInteger clickedRow = [tableView rowForView:popupButton];
//[...]
}
But the view property is not set automatically and I find it quite inconvenient to set it manually for every NSMenuItem.
Is there no easy way to get the table row?
Don't set an action and target for each NSMenuItem. Set the action and target for the NSPopupButton instead. Then, sender will be the NSPopupButton instance and you can use rowForView.
The drawback is of course that you cannot have different actions for different menu items.

How to get a reference to the view controller of a superview?

Is there a way to get a reference to the view controller of my superview?
There were several instances that I needed this on the past couple of months, but didn't know how to do it. I mean, if I have a custom button on a custom cell, and I wish to get a reference of the table view controller that controls the cell I`m currently in, is there a code snippet for that? Or is it something that I should just solve it by using better design patterns?
Thanks!
Your button should preferably not know about its superviews view controller.
However, if your button really needs to message objects that it shouldn't know the details about, you can use delegation to send the messages you want to the buttons delegate.
Create a MyButtonDelegate protocol and define the methods that everyone that conforms to that protocol need to implement (the callback). You can have optional methods as well.
Then add a property on the button #property (weak) id<MyButtonDelegate> so that any class of any kind can be set as the delegate as long as it conforms to your protocol.
Now the view controller can implement the MyButtonDelegate protocol and set itself as the delegate. The parts of the code that require knowledge about the view controller should be implemented in the delegate method (or methods).
The view can now send the protocol messages to its delegate (without knowing who or what it is) and the delegate can to the appropriate thing for that button. This way the same button could be reused because it doesn't depend on where it is used.
When I asked this question I was thinking of, in a situation where I have custom cells with buttons on them, how can the TableViewController know which cell's button was tapped.
More recently, reading the book "iOS Recipes", I got the solution:
-(IBAction)cellButtonTapped:(id)sender
{
NSLog(#"%s", __FUNCTION__);
UIButton *button = sender;
//Convert the tapped point to the tableView coordinate system
CGPoint correctedPoint = [button convertPoint:button.bounds.origin toView:self.tableView];
//Get the cell at that point
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:correctedPoint];
NSLog(#"Button tapped in row %d", indexPath.row);
}
Another solution, a bit more fragile (though simpler) would be:
- (IBAction)cellButtonTapped:(id)sender
{
// Go get the enclosing cell manually
UITableViewCell *parentCell = [[sender superview] superview];
NSIndexPath *pathForButton = [self.tableView indexPathForCell:parentCell];
}
And the most reusable one would be to add this method to a category of UITableView
- (NSIndexPath *)prp_indexPathForRowContainingView:(UIView *)view
{
CGPoint correctedPoint = [view convertPoint:view.bounds.origin toView:self];
return [self indexPathForRowAtPoint:correctedPoint];
}
And then, on your UITableViewController class, just use this:
- (IBAction)cellButtonTapped:(id)sender
{
NSIndexPath *pathForButton = [self.tableView indexPathForRowContainingView:sender];
}
If you know which class is the superview of your view controller, you can just iterate through the subviews array and typecheck for your superclass.
eg.
UIView *view;
for(tempView in self.subviews) {
if([tempView isKindOfClass:[SuperViewController class] ])
{
// you got the reference, do waht you want
}
}

Determine the last selected row when returning from detailView of UITableView

Ok I push a detailView when row is selected in the UITableView. In the detailView I set som data and pass that to the webservice. I want to retrieve some data either before the detailView is pushed or when it will disapear. Based on the data retrieved will I set an UIImage as an indicator.
I wonder how I can determine the indexpath.row the detailView got pushed for and use that for setting the UIImage. Is this possible?
I have a UISegmentedControl which loads two different datasources for the UITableView.
For testing I pass the indexpath.row when the detailView is pushed and callback to the UITableView class along with a boolean if the UIImage should be display.
But my problem is that indexpath.row is 0 when the UITableView loads.
And when changing datasource back and forth and passing the indexpath.row with it, the indicator will be display on wrong rows.
The two datasources for the table is:
if (index == 0) {
dictionary = [firstArray objectAtIndex:indexPath.row];
}
else {
dictionary = [secondArray objectAtIndex:indexPath.row];
}
I then set the indicator for that row (will be the UIImage later):
if (indexPath.row == selectedIndex) {
cell.indicator.text = #"Begin";
}
When the detailView is pushed in didSelectRowAtIndexPath i pass in the indexPath.row:
NSInteger rowIndex = indexPath.row;
[self.detailViewController initWithDetailsSelected:rowIndex:dictionary];
When the detailView is done the UITableView class receives the callback:
- (void)processAttendanceSuccessful:(BOOL)successful:(NSInteger)_selectedIndex{
self.selectedIndex = _selectedIndex;
}
So based on a boolean a UIImage should be display for the correct row in the UITableView.
Any suggestions how I can go about this. It must be quite common to set some value in a detailedView and display some data for a specific row in the UITableView, but having a rather hard time finding any good tutorials for this.
I only find how to pass data into the detailView, which I dont have any problem with.
Thanks in advance people.
Don't deselect the row in tableView:didSelectRowAtIndexPath:. When you return to this view controller, just use indexPathForSelectedRow method of the UITableView object.
But in the long run this makes little sense. Since the table view reuses those cells, you also need to remember the rows that have the image. You will probably have to maintain an array for this. In which case, you can either pass the array and the index path to the detail view controller and update them there or use delegate or notification mechanism to update the array from within the table view controller.
I would use an ivar of int, using negative values to represent null selection:
#interface MyController : UIViewController {
int lastSelection;
}
...
#end
#implementation MyController
...
- (void)initWithBlablabla{
...
lastSelection = -1;
}
...
#end
However, I think you should better re-think about your design referring to KVO programming guide. In general MVC patterns, a controller is not the right place to store the state of the model.

NSTableView keyDown: and mouseDown:

I've been working on a menubar note-taking application for Mac. It is written in Objective-C and Cocoa, and I'm using BWToolkit with it. My problem is getting keyDown: and mouseDown: events in a BWTransparentTableView which is a subclass of NSTableView. I just can't get it to work. I've tried searching the Internet, and some places say that you must subclass NSTableView. I've tried that, but it still doesn't work. I am pretty new to Objective-C and Cocoa, and I may just be doing something incorrectly.
Items in an NSTableView will automatically begin editing when they are slow double-clicked or when the Return key is pressed. Make sure that the table view, the cell and the array controller (if used) are marked as editable.
If you are not using an NSArrayController, make sure that your table view has a delegate and that it responds to tableView:shouldEditTableColumn:row:.
To handle a double click, you just need to set the doubleAction of the table view:
- (void)awakeFromNib
{
[tableView setTarget:self];
[tableView setDoubleAction:#selector(doubleClickInTable:)];
}
- (void)doubleClickInTable:(id)sender
{
NSInteger rowIndex = [sender selectedRow]; //Use selectedRowIndexes if you're supporting multiple selection
//Handle the double click
}
Note that neither of these methods require you to subclass NSTableView.