Who calls commitEditingStyle:forRowAtIndexPath? - objective-c

I've got a custom UITableViewCell that allows a user to input text when in edit mode. I've noticed that on stock UITableViewCells, when I swipe left, then hit the Delete button, it's table view receives the commitEditingStyle:forRowAtIndexPath message. I'm curious where this message is coming from. As far as I can tell, individual cells don't have a reference to the table view they belong to. If I want my custom cell to send this message, how would I go about doing that? I've thought about using the superview of the cell, but it seems like there's got to be an easier way.

This method gets called automatically (if you implement the data source protocol) before adding any row or column.
If you want to know to which table view a cell is belonging, use the superview method.

Related

Can't select rows in an NSTableView in Service app

I'm literally going nuts.
I have a service app that opens a pretty simple NSPanel that consists of an NSTableView, a label, and three NSButton controls.
That's it.
The table view is view-based, and has four different NSTableCellView rows defined in IB.
The table code is pretty straight forward; my NSWindowController subclass is both the data source and the delegate. And for the most part, everything works perfectly:
the window opens
the expected data source methods (numberOfRowsInTableView: and
tableView:objectValueForTableColumn:row:) get called
the table gets populated with its views and section separators via
tableView:isGroupRow: and tableView:viewForTableColumn:row:
empty selections are disabled, so the delegate immediately receives a
tableView:shouldSelectRow: message, and the table automatically
selects the first row
the table appears exactly as it should and the first row is selected
Everything looks perfect, but I can't select a different rows. Nothing I click on in the table changes the selection.
I have tried:
subclassing NSTableCellView and verified that it is receiving a hitTest: call
tried "hacking" the table cell view hitTest: so it always return nil
tried various combination of "refuses first responder", "enabled", "editable" properties on the control view inside the table cell view
tried deleting all of the control views. so the table cell views were empty
tried implementing tableView:selectionIndexesForProposedSelection: instead of tableView:shouldSelectRow:
changed the NSPanel into a regular NSWindow
Nothing seems to make any difference. Nothing I click on in the table changes the selection, and my table view delegate never receives another tableView:shouldSelectRow: call.
Note that all of the other (NSButton) controls in the window work just fine. I can click on any of them.
Update #1
Per the comment, I tried changing the product to a plain-old .app, but it makes no difference.
My next step was to fiddle some more with the delegate methods and have narrowed down the problem to this:
If I implement tableView:shouldSelectRow:, the table calls my delegate method twice (because I have have "empty selection" turned off, the table must initially determine which row select as a default, so I get two calls, one for row 0 (NO) and a second for row 1 (YES)). However, if I click in the table, I never receive another tableView:shouldSelectRow:.
If I remove the implementation of tableView:shouldSelectRow:, table selection magically starts to work. (Except for the fact that you can select group rows, which is worng.)
Also tried implementing tableView:selectionIndexesForProposedSelection: instead of tableView:shouldSelectRow:; same behavior
So it appears if I implement any of the delegate methods to determine which rows are selectable, I can't select any rows. sigh
Update #2
I punted and refactored the app so there is now a new view controller object dedicated to managing the table view (rather than overloading the window controller). All data model and delegate methods were moved to the new view controller.
Also (thinking there might be something weird with the table view in IB), I deleted the table view from the NIB and recreated it and all of its connections.
Unfortunately, neither of these changes made any difference. If the tableView:shouldSelectRow: is implemented, the table is unusable. Delete the method, and it works again.
Update #3
And it just get weirder: thinking I could "hack" the row selection problem, I implemented the table view delegate methods tableViewSelectionIsChanging: and tableViewSelectionDidChange:. Neither get called. Even if I remove tableView:shouldSelectRow:, allowing the table selection to work, no tableViewSelectionIsChanging: or tableViewSelectionDidChange: is ever received.
Yet, if I add observers for NSTableViewSelectionIsChangingNotification and NSTableViewSelectionDidChangeNotification, those are received.
Also note that the NSViewController subclass that is the table view's delegate and data source explicitly conforms to <NSTableViewDelegate> and <NSTableViewDataSource>, so there's shouldn't be any reason why the table view should be confused as to what delegate methods are implemented.
Yikes! Now this is embarrassing.
So the problem was a weak reference(s) to the window controller.
Here's what was happening: the window controller was being created, loaded, and the window was presented. During the initial presentation and display, everything worked (tableView:shouldSelectRow: et. al.) because the window and view controllers existed. But in some future event loop, ARC destroyed the window and view controllers, leaving only the window on the screen with a table view, and the weak references to its delegate and data source objects were now nil.
Solution was to fix the window controller management so it keeps a strong reference to the window controller until the window closes.
Sometimes it's the simplest things that trip you up...

How can I make command-A select all the NSTextView text in rows in an NSTableView?

So if I have an NSView based tableview and inside the views are NSTextViews which are non-editable but selectable...
how can I get that nice functionality of command-A selects all the text? I don't mean row selection. I have row selection disabled for the tableview. I mean highlighting the text in blue so you can copy it to your clipboard. But not just 1 NSTextView's text from one row, all of them from all the rows.
And in addition to command-A click and drag should do this too. But out of the box it seems I can only select one row's text. Here is video showing problem:
https://dl.dropboxusercontent.com/u/2510380/table.mov
(i keep clicking and dragging but can't highlight text on the next row)
here are two mac apps (skype and gabble) that do this:
https://dl.dropboxusercontent.com/u/2510380/skype.mov
and
https://dl.dropboxusercontent.com/u/2510380/gabble.mov
Assuming they are NOT using WebViews with just HTML inside, how do you get this control over the clipboard? i.e. in Skype you select the text and only the conversation is highlighted, not the timestamp of each message. Also the text copied to the clipboard is formatted very nicely. Can you point me in the right direction to reverse engineer skype?
Unfortunately there's no way to do this easily. This is because only ONE control can be the first responder at a time. This means that, though you can have selection in multiple text views, there are several problems:
Only one text view's text will actually be highlighted with the "live" highlight color; the others will have the gray highlight of non-focused controls.
Copy commands will only apply to the first responder text view.
Drag session starts will be initiated from the control the mouse was actually pointing at (irrespective of first responder) and will only drag that control's text.
In a view-based table view, the controls may not even "exist" for a row not currently displayed, so it'll never get the message unless you forcibly create each row, which could be costly for a large table.
Knowing all this, you might be able to "fake it" by having your controller be complicit in a text view and table view subclass's special handling of a select-all message when it's first responder. On receiving this message, the text view subclass can call super then notify the controller (to get its default behavior AND to let you know it happened), at which point the controller can turn around and send the command to all (existing) text views. Highlighting can be spoofed by overriding the text view's drawing and a drag initiation could defer to a delegate (the controller), which would handle writing ALL the strings from your model to the pasteboard (not even touching the text views in possibly-nonexistent row views). The table view subclass would simply pass the same select-all message to the controller without calling super (and even forcibly making sure nothing is selected before returning for good measure).
I hope this helps. If I've forgotten any of your requirements, let me know.
Try like this:-
First create button programatically then write this code after you create button and also write this code in your load method or awakefromnib method.
NSButton *Buttn=// alloc initwithframe;
[Buttn setKeyEquivalentModifierMask:
NSCommandKeyMask];
[Buttn setKeyEquivalent:#"A"];
[Buttn
setAction:#selector(yourmeth:)];
[Buttn setTarget:self];
// now when you press cmd a write
below code in action method
- (void)selectRowIndexes:(NSIndexSet
*)indexes byExtendingSelection:
(BOOL)extend

UITableView Cell - Edit?

Is it possible to use a UITableView to be able to enter a value into a database's field.
For example, if I was to have a UITableView pointing to a field within a database and if I wanted to enter a new entry into the database - tap on the UITableView Cell that would then allow keyboard input into the cell which ultimately end up being a new record in the database??
This is possible, but if something is possible doesn't mean you should be doing so.
You might ask why?
Well! you are trying to input data from view directly to database, this is a very bad practice. There are many reason for it being bad, the major is efficiency and security reasons.
You should consider using MVC pattern.
Now since its completely possible, I will explain the idea on how to do it and conclude with links that will have real code examples.
In tableView:cellForRowAtIndexPath:
add a TextField with tag (to get the reference back in future) and add it to contentView of the cell and have it hidden.
Now in tableView:didSelectRowAtIndexPath: make the cells editing property to YES.
Then, in tableView:willBeginEditingRowAtIndexPath:
get the reference to the textfield in contentview using viewWithTag: method and hide the textLabela and unhide the textfield.
In textfield's delegate textFieldDidEndEditing: make cell's editing property as no (yea, you need to keep the reference) unhide the textlabel and hide textfield.
In tableView:didEndEditingRowAtIndexPath: write methods which will commit the changes to your db.
Below are list of links which will get you code examples:
Having a UITextField in a UITableViewCell
Accessing UITextField in a custom UITableViewCell
iOS Database Tutorial
There are no examples for your requirement 'coz it bit bad way of doing things.
Yes its possible....
You can use delegate methods to take data form you cells textfield to your parent view controller and then save data in database.

How to remove cells that can be reused in UITableView (MonoTouch)

We have a scenario where we reuse the same UITableView to display different sets of records from the database as a user selects an item in a different UITableView (similar in concept to a splitviewcontroller, but all in the same view).
When the user selects a different area to view, we fetch the appropriate records from the database, update the Source with the new set of records, and call ReloadData.
The problem is that some of the cells in the UITableView still exist and are reused when switching between lists, resulting in invalid or overlapped data for various rows.
The question I have is: is there a method whereby we can tell the UITableView to discard all existing cells including those queued for reuse? I would have expected ReloadData to perform this task, but it does not.
Update with additional clarification:
Each cell in our table is composed of a variable number of subviews (we are trying to mimic a grid control). When the user selects a different list in the left-hand navigation, there is no guarantee that the subview positions (columns) in the newly selected list will overlap with those in the previous list. This is why we are looking for a method to remove the queued cells.
Since this is a situation that others may find themselves in, I will post the solution that we used, but am still interested in more "native" ways of doing this.
Here is how we solved the issue:
When the set of records being displayed is changed, we update the UITableView with a unique identifier for the current cell configuration (i.e. which list of data is the user currently viewing).
We then created a custom version of UITableViewCell and record the unique list identifier in the cell when it is created.
When we dequeue a reusable cell, we compare the view's current unique identifier with the unique identifier stored in the cell and, if they don't match, we discard the cell and create a new one.
Hopefully this will help someone down the road.
ReloadData does not clear the re-use queue. It simply triggers the callbacks to re-read the data which reloads the rows. As each cell get's loaded, if you're getting it out the re-use queue it will still be used. That's good even if the table view is re-used because the cell types are the same type especially since you use the CellIdentifier to ensure that.
But, it's kind of odd that you're overlapping data. When you get a cell out of the re-use queue, how are you putting the data in it? Are you painting the data directly on the cell? Are you adding subViews with the data (causing multiple sets of overlapped data)? Typically, a cell contains subviews like text labels and the data is set on them so there's no scenario where data gets overlapped - the subviews of cell just has their data updated ...
Your GetCell method needs to ensure that a dequeued cell is fully configured with the new data that it is going to display.
A common mistake is assuming that just dequeuing the cell for display will give you a fresh cell that you can use.
If you have done any customization with your cell, like adding views, modifies its properties (images, disclosure indicators, background colors) you must make sure in the GetCell method that is reusing a cell that every single one of those properties is properly set before returning the cell.

NSOutlineView elements remain hopelessly undraggable

I have a program with a NSOutlineView (that supports single selection only) from which I'd like to be able to drag elements. These elements should either be received as text or files: for instance, dropping the item on a TextEdit window should put text, but dropping the item on the Finder should create a file. I don't want anything to be dropped over my outline view, even it it comes from itself. This seems easy enough, but for some reason, I can't get it to work.
I checked the NSOutlineView drag and drop example from Apple, and I came to implement the following methods (plus a few definitely unrelated ones):
-(BOOL)outlineView:shouldSelectItem: // I don't expect to drag unselectable items
-(NSArray*)outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:
-(BOOL)outlineView:writeItems:toPasteboard:
However, when I try to drag an item from my outline view, nothing happens. Instead, it just changes the selection following the cursor.
I've put breakpoints in the two last methods, and they never get called, so their implementation is not the immediate issue.
I must be missing something really obvious here.
Also, this is not (yet) a problem, but how am I supposed to provide contents to my promised files?
I was being stupid and I implemented the methods in the delegate instead of the data source (the two are distinct in my app). Problem solved!
Are you using a custom table view cell? The result of NSCell's hitTestForEvent:inRect:ofView: determines whether a dragging operation can be initiated. It also determines whether your outlineView:writeItems:toPasteboard: should be called.
This method should return NSCellHitContentArea to initiate a drag, or NSCellHitTrackableArea to extend or change the selection.
A standard text cell returns NSCellHitContentArea when you click on the actual text of the cell, and NSCellHitTrackableArea when you click outside of the text. This produces the drag behavior you see in Finder's table view.
You can override this method and always return NSCellHitContentArea if you want all areas of the cell to initiate a drag operation.
See Hit Testing for more information.