delete particular tableview item in SWRevealViewController in objective C - objective-c

Hi how to remove particular table view cells in SWRevealViewController .my table View cells are hard coded in story board.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if(indexPath.row==2)
{
return 0.0;
}
if(indexPath.row==3)
{
return 0.0;
}
return 75;
}
While running first time its messed up.once click that particular cell its working properly. 
Here the Screen Shot: https://i.stack.imgur.com/YUNoi.jpg

Do as follows
1.Create Number of dynamic cells as per your needs(how much items you want to show).
2.Give different identifier to all of your cells.
3.Make a array of identifiers of all of yours cell in the sequence in which you want to show the cells. identifierArray[] in my case.
4.Then in numberOfItems inSection, return identifierArry.count.
5.Simply in cellForRow atIndexpath, dequeue your cell with identifier: identifierArray[indexpat.row].
6.if You want to delete any row then simply remove the identifier from IdentifierArray like so identifierArray.removeObject[position]. where position is the position of your item in array.
7.And after removing the identifier from array call tableView.reloadData method.
Hope it will help you.
Note: If it will solve your problem then not forget to mark this as accepted by checking the tick mark.

Related

collectionView:cellForItemAtIndexPath: doesn't get called

I want to add new cells in my collection view, but nothing shows up when I add data.
I have a custom UICollectionViewLayout class, which has been working just fine, and I've been keeping dummy data in my datasource to adjust the layout. Now that I got rid of the dummy data, nothing's showing up.
Since the app didn't break and there weren't any warnings, it was difficult to track down where the problem was, and here's where I found a clue:
(UICollectionViewLayout class)
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSLog(#"ElementsInRect: – Visible cells info: %#", [self.collectionView.visibleCells description]);
...
}
Here, -visibleCells returns an empty array, even when I add data, call -reloadData and invalidate the layout. So I placed a breakpoint in -collectionView:cellForItemAtIndexPath:, and it turns out this method is not called at all. How did the cells show up before?
Any help would be appreciated.
The data source method, collectionView:numberOfItemsInSection:, has to return a non-zero number for collectionView:cellForItemAtIndexPath: to be called. When you had dummy data in your data source, it was. Now that you removed that dummy data, that method is probably returning 0. When you add data, it should put items into your data source, and then a call to reloadData should work. You should put a log in collectionView:numberOfItemsInSection:, and see what it's returning.
Okay, it turns out the issue was in UICollectionViewLayout. I doubt anyone else will be having this problem, but I'll write my answer for the sake of completeness:
I'd been tweaking my custom UICollectionViewLayout class, and after I'd thought that it was working well, I made the code look neat by deleting old code that was commented out, move methods, etc.
While doing that, I recalled having read somewhere that it's good practice to create attributes in -prepareLayout method, and return those attributes when -layoutAttributesForItemAtIndexPath: or -layoutAttributesForElementsInRect: is called. For me, it was a matter of moving a block of code, so I thought no biggie. And during this "cleaning process" I must have made a mistake.
What's really frustrating is that the code itself actually works regardless of where the attributes are created, and I can't tell what went wrong for the last few days.
The following is a snippet of code that I used to create the attributes objects. My initial question was asking why -collectionView:cellForItemAtIndexPath: was not called while executing the 3rd line. I did not change this part of the code, other than moving it around.
for (int i = 0; i < 3; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:self.topLayer];
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
if (cell) {
UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
[self.array addObject:attributes];
} else {
NSLog(#"prepLayout: the cell doesn't exist for the index path {%d – %d}", indexPath.section, indexPath.item);
}
}
Number of Rows in Section - the count that can be used will determine if the cellForItemAtIndexPath gets called.
Initially when the view loads this will be called. Within the numberOfItemsInSection, if you have an array, the [array count] might return a nil value.
Complete the procedure where the array is populated, then reload the data in the collection view which will re-assess the numberOfItemsInSection. This can be done with the following code:
[self.myCollectionView reloadData];
"myCollectionView is the name given to the collection view item in your view"

indexPath Value of UICollectionView

When using a UICollectionView, I am perplexed with getting the indexPath value of the didSelectItemAtIndexPath method.
I'm using the following line of code in the didSelectItemAtIndexPath method:
//FileList is an NSArray of files from the Documents Folder within my app.
//This line gets the indexPath of the selected item and finds the same index in the Array
NSString *selectedItem = [NSString stringWithFormat:#"%#",[FileList objectAtIndex:indexPath.item]];
//Now the selected file name is displayed using NSLog
NSLog(#"Selected File: %#", selectedItem);
The problem is that the indexPath always returns 0 (see below code) and as a result only the first item in the FileList NSArray is ever selected.
I've tried different parameters such as
indexPath.row
indexPath.item
and just plain indexPath
ALL of these return a value of 0 in the following NSLog statement:
NSLog(#"index path: %d", indexPath.item); //I also tried indexPath.row here
Maybe I'm just formatting the NSString improperly, however I don't think this is the case as there are no warnings and I've tried formatting it differently in other places.
Why does the indexPath always return 0?
Any help would be appreciated! Thanks!
At the risk of stating the obvious, make sure your code is in the didSelectItemAtIndexPath method rather than didDeselectItemAtIndexPath. You will get very different indexPath values for a given touch event in the latter method and it's quite easy to insert the wrong one with Xcode's code completion.
The delegate method you are using always provides the index path of the cell that was selected.
Try to debug with a breakpoint on your NSLog call. When debugger stops on it look at the Debug Area at the bottom and use the console (View => Debug Area => Activate Console if you need it).
Type in the console: po indexPath
You should see something like this if selected item is the 5th of the list in section:0 (first section and probably the only one)
<NSIndexPath 0xa8a6050> 2 indexes [0, 5]
Hope this helps to figure out what's going on.

UITableView multiselect

I have a uitableview with 50 rows populated from a predefined nsarray.
How can I select multiple the rows with say maximum 3 allowed at a time and show check when selected and remove check when deselected/
I am really new to xcode and any help is much appreciated. Thank you.
Your data needs to keep track of whether it is selected or not.
Two common ways are: each object in your predefined array has a BOOL that indicates whether or not it is selected, or you keep a second array that holds only references to selected objects. Since you're limited to three selected, the second option might be better.
When someone selects a cell in your table, you change the selection status of the related object, either switching its BOOL or adding/removing it in the extra array. This is also the place to check whether you already have as many selections as you allow. If selections have changed, you then tell your table to reload data.
In cellForRowAtIndexPath: you check whether or not the object is selected and mark it accordingly.
int counter = 0; //keep track of how many rows are selected
int maxNum = 3; //Most cells allowed to be selected
//Called when the user selects a row
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
//If the cell isn't checked and there aren't the maximum allowed selected yet
if (cell.accessoryType != UITableViewCellAccessoryCheckmark && counter < maxNum)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
counter++;
}
else if (counter >= maxNum) return; //Don't do anything if the cell isn't checked and the maximum has been reached
else //If cell is checked and gets selected again, deselect it
{
cell.accessoryType = UITableViewCellAccessoryNone;
counter--;
}
}
You might also want to keep an array of indices of the cells that are selected, in case you want to do something with the data that's in them. If you don't know how to do this, let me know and I'll add the code.
Notes:
You need to be implementing the table view delegate protocol in order to have this method called correctly.
This isn't the "best" way to do it (using cell content to keep track of selection is generally frowned up) but it is very easy.
You might run into problems with cell reuse. If you want to fix that, store the cell's index and set the accessory type in cellForRowAtIndexPath

Opening a gap in NSTableView during drag and drop

I've got a simple, single-column, view-based NSTableView with items in it that can be dragged to reorder them. During drag and drop, I'd like to make it so that a gap for the item-to-be-dropped opens up at the location under the mouse. GarageBand does something like this when you drag to reorder tracks (video here: http://www.screencast.com/t/OmUVHcCNSl). As far as I can tell, there's no built in support for this in NSTableView.
Has anyone else tried to add this behavior to NSTableView and found a good solution? I've thought of and tried a couple approaches without much success. My first thought was to double the height of the row under the mouse during a drag by sending -noteHeightOfRowsWithIndexesChanged: in my data source's -tableView:validateDrop:... method, then returning twice the normal height in -tableView:heightOfRow:. Unfortunately, best I can tell, NSTableView doesn't update its layout during drag and drop, so despite calling noteHeightOfRowsWithIndexesChanged:, the row height isn't actually updated.
Note that I'm using a view-based NSTableView, but my rows are not so complex that I couldn't move to a cell-based table view if doing so helped accomplish this. I'm aware of the easy, built-in ability to animate a gap for the dropped item after a drag is complete. I'm looking for a way to open a gap while the drag is in progress. Also, this is for an app to be sold in the Mac App Store, so it must not use private API.
EDIT: I've just filed an enhancement request with Apple requesting built in support for this behavior: http://openradar.appspot.com/12662624. Dupe if you'd like to see it too. Update: The enhancement I requested was implemented in OS X 10.9 Mavericks, and this behavior is now available using NSTableView API. See NSTableViewDraggingDestinationFeedbackStyleGap.
I feel bizarre for doing this, but there's an extremely thorough answer in the queue here that appears to have been deleted by its author. In it, they provided the correct links to a working solution, which I feel need to be presented as an answer for someone else to take and run with, inclusive of them if they desire to do so.
From the documentation for NSTableView, the following caveats are tucked away for row animation effects:
Row Animation Effects
Optional constant that specifies that the tableview will use a fade for row or column removal. The effect can be combined with any NSTableViewAnimationOptions constant.
enum {
NSTableViewAnimationEffectFade = 0x1,
NSTableViewAnimationEffectGap = 0x2,
};
Constants:
...
NSTableViewAnimationEffectGap
Creates a gap for newly inserted rows. This is useful for drag and drop animations that animate to a newly opened gap and should be used in the delegate method tableView:acceptDrop:row:dropOperation:.
Going through the example code from Apple, I find this:
- (void)_performInsertWithDragInfo:(id <NSDraggingInfo>)info parentNode:(NSTreeNode *)parentNode childIndex:(NSInteger)childIndex {
// NSOutlineView's root is nil
id outlineParentItem = parentNode == _rootTreeNode ? nil : parentNode;
NSMutableArray *childNodeArray = [parentNode mutableChildNodes];
NSInteger outlineColumnIndex = [[_outlineView tableColumns] indexOfObject:[_outlineView outlineTableColumn]];
// Enumerate all items dropped on us and create new model objects for them
NSArray *classes = [NSArray arrayWithObject:[SimpleNodeData class]];
__block NSInteger insertionIndex = childIndex;
[info enumerateDraggingItemsWithOptions:0 forView:_outlineView classes:classes searchOptions:nil usingBlock:^(NSDraggingItem *draggingItem, NSInteger index, BOOL *stop) {
SimpleNodeData *newNodeData = (SimpleNodeData *)draggingItem.item;
// Wrap the model object in a tree node
NSTreeNode *treeNode = [NSTreeNode treeNodeWithRepresentedObject:newNodeData];
// Add it to the model
[childNodeArray insertObject:treeNode atIndex:insertionIndex];
[_outlineView insertItemsAtIndexes:[NSIndexSet indexSetWithIndex:insertionIndex] inParent:outlineParentItem withAnimation:NSTableViewAnimationEffectGap];
// Update the final frame of the dragging item
NSInteger row = [_outlineView rowForItem:treeNode];
draggingItem.draggingFrame = [_outlineView frameOfCellAtColumn:outlineColumnIndex row:row];
// Insert all children one after another
insertionIndex++;
}];
}
I'm unsure if it's really this simple, but it's at least worth inspection and outright refutal if it doesn't meet your needs.
Edit: see this answer's comments for the steps followed to the right solution. The OP has posted a more complete answer, which should be referred to by anyone looking for solutions to the same problem.
Note: The behavior this question and answer describes are now available using built in API in NSTableView on OS X 10.9 Mavericks and later. See NSTableViewDraggingDestinationFeedbackStyleGap.
This answer may still be useful if this behavior is needed in an app targeting OS X 10.8 or earlier.
Original answer below:
I've implemented this now. My basic approach looks like this:
#interface ORSGapOpeningTableView : NSTableView
#property (nonatomic) NSInteger dropTargetRow;
#property (nonatomic) CGFloat heightOfDraggedRows;
#end
#implementation ORSGapOpeningTableView
#pragma mark - Dragging
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
NSInteger oldDropTargetRow = self.dropTargetRow;
NSDragOperation result = [super draggingUpdated:sender];
CGFloat imageHeight = [[sender draggedImage] size].height;
self.heightOfDraggedRows = imageHeight;
NSMutableIndexSet *changedRows = [NSMutableIndexSet indexSet];
if (oldDropTargetRow > 0) [changedRows addIndex:oldDropTargetRow-1];
if (self.dropTargetRow > 0) [changedRows addIndex:self.dropTargetRow-1];
[self noteHeightOfRowsWithIndexesChanged:changedRows];
return result;
}
- (void)draggingExited:(id<NSDraggingInfo>)sender
{
self.dropTargetRow = -1;
[self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];
[super draggingExited:sender];
}
- (void)draggingEnded:(id<NSDraggingInfo>)sender
{
self.dropTargetRow = -1;
self.heightOfDraggedRows = 0.0;
self.draggedRows = nil;
[self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
self.dropTargetRow = -1;
self.heightOfDraggedRows = 0.0;
self.draggedRows = nil;
[self noteHeightOfRowsWithIndexesChanged:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self numberOfRows])]];
return [super performDragOperation:sender];
}
// In my delegate and data source:
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id<NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation
{
if (dropOperation == NSTableViewDropOn)
{
dropOperation = NSTableViewDropAbove;
[self.tableView setDropRow:++row dropOperation:dropOperation];
}
NSDragOperation result = [self.realDataSource tableView:tableView validateDrop:info proposedRow:row proposedDropOperation:dropOperation];
if (result != NSDragOperationNone)
{
self.tableView.dropTargetRow = row;
}
else
{
self.tableView.dropTargetRow = -1; // Don't open a gap
}
return result;
}
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
{
CGFloat result = [tableView rowHeight];
if (row == self.tableView.dropTargetRow - 1 && row > -1)
{
result += self.tableView.heightOfDraggedRows;
}
return result;
}
Note that this is simplified code, not a verbatim copy/paste from my program. I actually ended up making this all contained within an NSTableView subclass that uses proxy delegate and data source objects so the code in data source/delegate methods above is actually inside the proxies' intercept of the calls to the real delegate and data source. That way, the real data source and delegate don't have to do anything special to get the gap opening behavior. Also, there's sometimes a little flakiness with the table view animations, and this doesn't work for drags above the first row (no gap is opened since there's no row to make taller). All in all, despite the room for further improvement, this approach works reasonably well.
I'd still like to try a similar approach, but insert a blank row (as Caleb suggested) instead of changing the row height.
As of Mac OS X 10.9 (Mavericks), there's a much easier solution to animating drag & drop in a NSTableView:
[aTableView setDraggingDestinationFeedbackStyle:NSTableViewDraggingDestinationFeedbackStyleGap];
The table view will automatically insert gaps with animation as a row is dragged which is much nicer than the old blue line insertion point method.
One way to accomplish what you're asking is to insert an empty row at the proposed drop point (that is, between the two nearest rows). It sounds like you've been looking at using NSTableViewAnimationEffectGap, which as you note is really meant for animating the insertion when the drop is accepted in -tableView:acceptDrop:row:dropOperation:.
Since you want to open up the gap before the user releases the mouse button to actually do the drop, you could instead insert a blank row using -insertRowsAtIndexes:withAnimation: from your table's -draggingUpdate: method and at the same time delete any blank row you previously inserted for this drag using -removeRowsAtIndexes:withAnimation:. Use NSTableViewAnimationSlideUp and NSTableViewAnimationSlideDown as the animations for these operations, as appropriate.

reloadData in NSTableView but keep current selection

I have anNSTableView showing the contents of a directory. I watch for FSEvents, and each time I get an event I reload my table view.
Unfortunately, the current selection then disappears. Is there a way to avoid that?
Well, you can save selection before calling reloadData and restore it after that.
NSInteger row = [self.tableView selectedRow];
[self.tableView reloadData];
[self.tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
This worked for me in some cases. But if you insert some items BEFORE the selected row, you should andjust your row variable by adding count of added items to it.
Swift 4.2
Create an extension and add a method which preserves selection.
extension NSTableView {
func reloadDataKeepingSelection() {
let selectedRowIndexes = self.selectedRowIndexes
self.reloadData()
self.selectRowIndexes(selectedRowIndexes, byExtendingSelection: false)
}
}
Do this in case you use the traditional way of populating table views (not NSArrayController).
It depends on how you populate your NSTableView.
If you have the table view bound to an NSArrayController, which in turn contain the items that your table view is displaying, then the NSArrayController has an option to preserve the selection. You can select it (or not) from within Interface Builder as a property on the NSArrayController. Or you can use the setPreservesSelection method in code.
However, if you completely replace the array of items that the NSArrayController manages each time you get your FSEvents, then maybe the preservation of selection cannot work. Unfortunately the Apple docs on this property of NSArrayController are a bit vague as to when it can and cannot preserve the selection.
If you are not using an NSArrayController, but maybe using a dataSource to populate the table view, then I think you'll have to manage the selection yourself.
In the case of using Data Source, Apple Documentation in the header file on reloadData() is that
The selected rows are not maintained.
To get around, you can use reloadDataForRowIndexes(rowIndexes: NSIndexSet, columnIndexes: NSIndexSet). As mentioned in the same header file
For cells that are visible, appropriate dataSource and delegate methods will be called and the cells will be redrawn.
Thus the data will be reloaded, and the selection is kept as well.
A variant on #silvansky's answer.
This one has no need to keep track of count of items inserted/deleted. And it maintains multiple selection.
The idea is to...
1. create an array of selected objects/nodes from the current selection.
2. refresh the table using reloadData
3. for each object obtained in step 1, find/record it's new index
4. tell the table view/outline view to select the updated index set
- (void)refresh {
// initialize some variables
NSIndexSet *selectedIndexes = [self.outlineView selectedRowIndexes];
NSMutableArray *selectedNodes = [NSMutableArray array];
NSMutableIndexSet *updatedSelectedIndex = [NSMutableIndexSet indexSet];
// 1. enumerate all selected indexes and record the nodes/objects
[selectedIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
[selectedNodes addObject:[self.outlineView itemAtRow:idx]];
}];
// 2. refresh the table which may add new objects/nodes
[self.outlineView reloadData];
// 3. for each node in step 1, find the new indexes
for (id selectedNode in selectedNodes) {
[updatedSelectedIndex addIndex:[self.outlineView rowForItem:selectedNode]];
}
// 4. tell the outline view to select the updated index set
[self.outlineView selectRowIndexes:updatedSelectedIndex byExtendingSelection:NO];
}