writeRowsWithIndexes is not calling in NSTableView with custom cell - objective-c

I have implemented following delegate to support drag n drop in NSTableView. Delegates validateDrop and acceptDrop are called and I'm able to drag and drop content from Finder to NSTableView however writeRowsWithIndexes is not called when I tried to drag items from NSTableView and drop to Finder.
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
{
NSLog(#"writeRowsWithIndexes");
return YES;
}
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
{
NSLog(#"validateDrop");
return NSDragOperationCopy;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id < NSDraggingInfo >)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
NSLog(#"acceptDrop");
return YES;
}
I have registered dragging types as
//Registering dragged Types
[tableView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
//To support across application passing NO
[tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
//Don't want any highlight selection on drop
[tableView setDraggingDestinationFeedbackStyle:NSTableViewDraggingDestinationFeedbackStyleNone];
Drag is not initiating in my NSTableView, Can anybody tell me what could be the possible reasons? I have custom cell in the TableView and also set delegate and datasource properly.

I found the cause, it was due to Custom cell. I had subclassed my custom cell from NSButtonCell. Dragging is not working on button cell because buttons are not meant for dragging, they are meant for illuminating.
I resolved the issue by sub classing my custom cell from NSActionCell.
Found in the below post:
NSTableView Drag and Drop not working

Related

Cannot get drag and drop to work onto NSCollectionView

There is probably a simple mistake that I'm making, but I simply cannot get dropping of files onto an NSCollectionView to work even in the most basic way.
In a test project, I have an NSCollectionView on a window, and the view controller is both its delegate and data source. I want to be able to drag files from the Finder onto this collection view.
From reading the docs, all I should have to do is:
Register for dragged type(s):
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"Registering dragged types for collection view: %#", self.collectionView);
[self.collectionView registerForDraggedTypes:#[NSFilenamesPboardType]];
[self.collectionView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
[self.collectionView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO];
}
And then implement these two methods:
-(NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id<NSDraggingInfo>)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation {
NSLog(#"Validate drop: %#", draggingInfo);
return NSDragOperationMove;
}
-(BOOL)collectionView:(NSCollectionView *)collectionView acceptDrop:(id<NSDraggingInfo>)draggingInfo index:(NSInteger)index dropOperation:(NSCollectionViewDropOperation)dropOperation {
NSLog(#"Accept drop: %#", draggingInfo);
return YES;
}
But none of the two methods is ever called, when I try to drag an item onto the collection view, which makes me think that the registerForDraggedTypes: call is not working as expected.
What can be the issue here? What else do I have to look into?
From OS X 10.11 the NSCollectionViewDelegate methods take an index path instead of an index. For instance in
-(NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id<NSDraggingInfo>)draggingInfo proposedIndex:(NSInteger *)proposedDropIndex dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation
the proposedIndex: parameter is replaced by proposedIndexPath:
- (NSDragOperation)collectionView:(NSCollectionView *)collectionView validateDrop:(id <NSDraggingInfo>)draggingInfo proposedIndexPath:(NSIndexPath * __nonnull * __nonnull)proposedDropIndexPath dropOperation:(NSCollectionViewDropOperation *)proposedDropOperation

Make 'edit' button trigger edit-mode on UITableView

This might be a very stupid question, but I have very little knowledge about Xcode, Objective-C and iOS development in general.
What I have is a UITableViewController, with a number of cells. I've gotten the 'add' and delete (swipe to delete) functions to work, but I cannot figure out how to connect the 'edit' button to a function that will trigger the editing mode of that TableView, so I can rearrange the order of the elements.
I have un-commented these functions:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
XYZToDoItem *itemToMove = [self.toDoItems objectAtIndex:fromIndexPath.row];
[self.toDoItems removeObjectAtIndex:fromIndexPath.row];
[self.toDoItems insertObject:itemToMove atIndex:toIndexPath.row];
[self.tableView reloadData];
}
As you can see I have also made a few additions to the moveRowAtIndexPath method following the documentation.
In addition there are these functions;
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.toDoItems removeObjectAtIndex:indexPath.row];
[self.tableView reloadData];
}
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
As previously stated the "swipe to delete" function works, I don't know how, but I simply just uncommented these last two methods and edited one a bit to fit my code.
To be more specific, what I'm asking is how can I link the 'edit' button I have in my Navigation Controller to trigger the edit mode for the TableView, thereby showing the three lines (?) known from the TableViews editing mode (and letting me drag the elements to an order I want).
Thanks in advance for all your help! :)
If this is a UITableViewController you have two options. Either use the built-in edit button, which will give you the automatic Edit/Done button change. Use self.editButtonItem in your navigation bar:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
Or you could use setEditing:animated to change editing state of the tableViewController:
// self is a UITableViewController
[self setEditing:YES animated:YES];
If your viewController is not a subclass of UITableViewController you can set the editing mode of the tableView:
[self.tableView setEditing:YES animated:YES];
If you have a UITableViewController you should use the first way because it is most convenient.

NSTableView no selection highlight, but keep track of what row is selected

I've seen some threads with a similar question to this (not exactly the same) but the solutions there didn't solve my problem. I've create a NSTableView with a couple clickable elements in each TableCellView -- a TextField, a DatePicker and a Button -- and I need to be able to click into each of these without first selecting the row they are in. I achieved this by using setSelectionHighlightStyle: NSTableViewSelectionHighlightStyleNone However, I still need to know the index of the row that is being edited, because I need to pass the edits into a model object associated with each row. I tried writing the following, which was the suggestion of some of the other threads I mentioned, but it didn't do anything.
- (BOOL)tableView:(NSTableView *)tableView shouldTrackCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row{
return YES;
}
I'm not 100% sure if that's the wrong method to use for this, or if I'm just implementing it incorrectly. Any tips on how I could go about this would be awesome.
Edit:
Here's my attempt at implementing Steve Waddicor's suggestions:
Making the ViewController the NSTableViewDataSource and set the TableCellView as the delegates for the objects (code is in MyViewController.m):
- (void)awakeFromNib{
[self.tableView setDataSource:self];
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
CustomTableCellView *view = [self.tableView makeViewWithIdentifier:#"MyIdentifier" owner:self];
[view.textField setDelegate:view];
[view.datePicker setDelegate:view];
return view;
}
//-----------------------
//-----------------------
//----------------------- NSTableViewDataSource Protocol Requirements:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return [self.tableView numberOfRows];
}
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
return [self.tableView rowViewAtRow:rowIndex makeIfNecessary:NO];
}
and in CustomTableCellView, this is the delegate method which isn't being called when I click on the TextField, which leads me to believe that the delegates aren't being set for the objects correctly:
- (void)mouseDown:(NSEvent*) theEvent{
NSLog(#"TEST");
}
You could set the your tableCellView as the delegate of the controls. From there you could
NSTableView* tableView = self.superview;
NSUInteger row = [tableView rowForView:self];
Edit
To make a delegate. In your tableView dataSource you have a method something like this:
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
MyTableCellView* view = [outlineView makeViewWithIdentifier:#"MyIdentifier" owner:self];
[view.textField setDelegate:view];
[view.datePicker setDelegate:view];
[view.button setDelegate:view];
return view;
}
try this delegate
- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(NSInteger)rowIndex
{
return YES;
}

Drag&Drop is not enabled for a NSOutlineView although I've implemented delegate methods

I'm not able to enable drag and drop for a NSOutlineView. I've implemented the related method of the NSOutlineView Delegate.
But it seems that when I click an item, I can't even dragging it (I don't see animation).
- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index
{
return YES;
}
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
{
return NSDragOperationMove; //not sure about this one.
}
thanks
UPDATE:
I'm implementing forOSX >= 10.5
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
{
NSString *pasteBoardType = [self pasteboardTypeForTableView:outlineView];
[pboard declareTypes:[NSArray arrayWithObject:pasteBoardType] owner:self];
NSData *rowData = [NSKeyedArchiver archivedDataWithRootObject:items];
[pboard setData:rowData forType:pasteBoardType];
return YES;
}
The methods you implemented are just for the destination of the drag. You still need to implement the dragging source methods. For whatever reason Apple's NSOutlineViewDataSource Protocol documentation is missing these methods but you have two options:
If you are building 10.7+ use Xcode's Open Quickly command to look in NSOutlineView.h and find the relevant methods. Also check out the DragNDropOutlineView sample app.
If you are supporting previous OS's then use NSTableView's delegate methods. See NSTableViewDataSource Protocol Reference. Remember that NSOutlineView is a subclass of NSTableView and can use the table view methods.
At a minimum you will probably want to implement outlineView:writeItems:toPasteboard:
/* Dragging Source Support - Optional for single-image dragging. This method is called after
it has been determined that a drag should begin, but before the drag has been started. To
refuse the drag, return NO. To start a drag, return YES and place the drag data onto the
pasteboard (data, owner, etc...). The drag image and other drag related information will
be set up and provided by the outline view once this call returns with YES. The items array
is the list of items that will be participating in the drag.
*/
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard;
Update:
If the item can be dragged but won't drop on anything then most likely outlineView:validateDrop:proposedItem:proposedChildIndex: is not being called. This would mean you haven't registered the pasteboard type which you do with registerForDraggedTypes:. You would do this somewhere in the view controller, probably in awakeFromNib.
[outlineView registerForDraggedTypes:[NSArray arrayWithObject:#"myPasteBoardType"]];
To move the item (and all its children) modify your model in outlineView:acceptDrop:item:childIndex:. Then send reloadData to the outlineView.
To make your outline view a dragging source, you must implement:
- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pasteboard;
This should address what you described, but you've got a lot more work to do beyond this.

How to unselect a UITableViewCell after you have done some action using didSelectRowAtIndexPath?

Currently I do some work inside the didSelectRowAtIndexPath delegate and noticed that my row is still "selected" after I come back to the view controller later.
How can I unselect this row inside the delegate itself?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//do logic and ... it remains selected in the view?
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Typically, you want to deselect the row when the view controller appears again:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
That's what UITableViewController does implicitly, and animating the deselection process after the view controller appears again is a helpful hint to the user as to which cell they selected previously.