Implementing drag and drop in NSTableView - objective-c

Can anyone help me to implement drag and drop in an NSTableView? I used this code below, but these methods are not getting called during execution.
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
{
// Copy the row numbers to the pasteboard.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:[NSArray arrayWithObject:#".gif"] owner:self];
[pboard setData:data forType:#".gif"];
return YES;
}
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op
{
// Add code here to validate the drop
if (row > [ m_imageArray count])
return NSDragOperationNone;
if (nil == [info draggingSource]) // From other application
{
return NSDragOperationNone;
}
else if (self == [info draggingSource]) // From self
{
return NSDragOperationNone;
}
else // From other documents
{
[tv setDropRow: row dropOperation: NSTableViewDropAbove];
return NSDragOperationCopy;
}
NSLog(#"validate Drop");
return NSDragOperationCopy;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id <NSDraggingInfo>)info
row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
{
NSPasteboard* pboard = [info draggingPasteboard];
NSData* rowData = [pboard dataForType:#".gif"];
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
NSInteger dragRow = [rowIndexes firstIndex];
// Move the specified row to its new location...
}

Here is an example
#import "TableViewController.h"
#import "Person.h"
#define MyDataType #"MyDataType"
#implementation TableViewController {
NSMutableArray *list;
NSInteger sourceIndex;
}
#synthesize tableView;
-(void)awakeFromNib {
[tableView registerForDraggedTypes:[NSArray arrayWithObjects:MyDataType, nil]];
list = [[NSMutableArray alloc] init];
Person *person = [[Person alloc] initWithName:#"Newton" age:64];
[list addObject:person];
person = [[Person alloc] initWithName:#"Archimedes" age:74];
[list addObject:person];
person = [[Person alloc] initWithName:#"Euler" age:44];
[list addObject:person];
person = [[Person alloc] initWithName:#"Poincare" age:24];
[list addObject:person];
person = [[Person alloc] initWithName:#"Gauss" age:34];
[list addObject:person];
}
-(void)reArrange:(NSMutableArray *)array sourceNum:(NSInteger)sourceNum destNum:(NSInteger)destNum {
Person *person = list[sourceNum];
[list insertObject:person atIndex:destNum];
if (sourceNum < destNum) {
[list removeObjectAtIndex:sourceNum];
} else {
[list removeObjectAtIndex:sourceNum+1];
}
[tableView reloadData];
}
#pragma mark - Table
// how many rows are there in the table?
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tv {
return list.count;
}
// What object should I show in a particular cell?
-(id)tableView:(NSTableView *)tv objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
Person *person = list[row];
NSString *identifier = [tableColumn identifier];
return [person valueForKey:identifier];
}
// Should I accept the drag with the rows specified by rowIndexes? If YES then place the data on the provided paste board.
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:[NSArray arrayWithObject:MyDataType] owner:self];
[pboard setData:data forType:MyDataType];
sourceIndex = [rowIndexes firstIndex];
return YES;
}
// What kind of drag operation should I perform?
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op {
return op == NSTableViewDropAbove; // Specifies that the drop should occur above the specified row.
}
// The mouse button was released over a row in the table view, should I accept the drop?
- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)op {
[self reArrange:list sourceNum:sourceIndex destNum:row]; // let the source array reflect the change
return YES;
}
#end

You need to declare a custom drag type for your table view and then call registerForDraggedTypes: with your custom type. Otherwise, as you have noticed, none of these methods will get called.

Drag and Drop NSTableview using core data
Register table-view object for drag and drop:-
[tblCategory registerForDraggedTypes:[NSArray arrayWithObject:#"public.text"]];
Drag and drop Delegate methods:-
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView pasteboardWriterForRow:(NSInteger)row
{
Category *category = (Category *)[self.arrCategoryList objectAtIndex:row];
NSString *identifier = category.categoryname;
NSPasteboardItem *pboardItem = [[NSPasteboardItem alloc] init];
[pboardItem setString:identifier forType: #"public.text"];
return pboardItem;
}
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation
{
if(dropOperation == NSTableViewDropOn)
{
NSPasteboard *p = [info draggingPasteboard];
NSString *title = [p stringForType:#"public.text"];
Category* category ;
NSInteger srcIndex;
for (srcIndex = 0; srcIndex < [_arrCategoryList count]; srcIndex++)
{
category = [_arrCategoryList objectAtIndex:srcIndex];
if ([category.categoryname isEqualToString:title])
{
break;
}
category = nil;
}
if(!category)
{
// Not found
return NO;
}
[_arrCategoryList removeObjectAtIndex:srcIndex];
[_arrCategoryList insertObject:category atIndex:row];
[tblCategory reloadData];
return NSDragOperationMove;
}
return NSDragOperationNone;
}
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation
{
return YES;
}

I usually observe this kind of error when I forgot to hook up the dataSource to the NSTabeView in IB, i.e. the class implementing the tableView:writeRowsWithIndexes:toPasteboard: method of the NSTableViewDataSource.

You can register for draggedTypes for an NSMutableArray or NSMutableDictionary or any other object.Following Code snippet is for a NSMutableArray.
[tableView registerForDraggedTypes:[NSArray arrayWithObject:#"NSMutableArray"] ];

You need to declare a registerForDraggedTypes method in awakFromnib
like this.
[table_view registerForDraggedTypes:[NSArray arrayWithObjects:BasicTableViewDragAndDropDataType, nil]];

I have to recommend both this excellent Red Sweater blog post
http://www.red-sweater.com/blog/274/a-moveable-beast
It provides an NSArrayController sub-class which will enable drag and drop re-ordering of table items, and if you want to support dragging outside of the tableview with your objects, we've just written up a neat and simple addition to that class:
http://www.rwe-uk.com/blog/comments/nstableview_drag_and_drop_with_bindings_and_nsarraycontroller
It builds on the original post

Related

Drag and Drop Custom TableViewCell of NSTableView

#define MyPrivateTableViewDataType #"MyPrivateTableViewDataType"
//add line in your awakeFormNib method
[self.tableView registerForDraggedTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType]
// This method is called
-(BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard {
// Copy the row numbers to the pasteboard.
NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pboard declareTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType] owner:self];
[pboard setData:zNSIndexSetData forType:MyPrivateTableViewDataType];
return YES;
}
***following methods are not called***
- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op {
// Add code here to validate the drop
return NSDragOperationEvery;
}
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id )info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation {
NSPasteboard* pboard = [info draggingPasteboard];
NSData* rowData = [pboard dataForType:MyPrivateTableViewDataType];
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
NSInteger dragRow = [rowIndexes firstIndex];
if (dragRow < row) {
[model insertObject:[model objectAtIndex:dragRow] atIndex:row];
[model removeObjectAtIndex:dragRow];
[aTableView noteNumberOfRowsChanged];
[aTableView moveRowAtIndex:dragRow toIndex:row-1];
}else {
ModelObj *obj = [model objectAtIndex:dragRow];
[model removeObjectAtIndex:dragRow];
[model insertObject:obj atIndex:row];
[aTableView noteNumberOfRowsChanged];
[aTableView moveRowAtIndex:dragRow toIndex:row];
}
return YES;
}
My NSTableView have custom tableViewCell , I registered DraggedTypes. i set delegate and datasource of table. but Out of 3 methods only first method -(BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard is called.
why other methods are not called.

How to add Pagination to Tableview on Search results?

The following lines of code shown below are my current tableview settings. What i would like to achieve is what's seen on the image below.
1) if count is 4 when user scrolls Down show a Activity Indicator and Make a request using the next value shown on the JSON Below.
2) Reload the Table
3) Show No more results if count is empty.
Note: I am new on Objective-C so I will appreciate your guidance on this subject since all searches I have done were not successful on what I am looking to achieve since all of them are based on a table already loaded and not based on a Search Result.
As shown on the Image Below is a reference what i want to achieve.
JSON RESPONSE FROM SEARCH
{
"count": 4,
"next": "http://api.domain.com/user-search/?page=2&subject=culture",
"previous": null,
"results": [
{
"name": "Guillermo Davila",
"nick": "guillermo",
"avatar_s": "https://pbs.twimg.com/profile_images/2213685686/image.jpg",
"user_rate": "$10/h",
"id": 3,
"subjects": "Culture and 1 other subject",
"bio": "I'm a nice person"
},
{
"name": "Frank Smith",
"nick": "fsmith",
"avatar_s": "https://pbs.twimg.com/profile_images/2444486/image.jpg",
"user_rate": "$14/h",
"id": 3,
"subjects": "Culture and 1 other subject",
"bio": "I'm a nice person 2"
}
]
}
And here is my UserTableviewController.h
//
// UserTableViewController.m
// mobile-app
//
// Created by eddwinpaz on 5/18/14.
// Copyright (c) 2014 eddwinpaz. All rights reserved.
//
#import "UserTableViewController.h"
#import "CustomTableCell.h"
#import "UserDetailViewController.h"
#import "AFNetworking.h"
#import "MBProgressHUD.h"
#import "SimpleAudioPlayer.h"
#interface UserTableViewController ()
#end
#implementation UserTableViewController
{
// JSON Request
NSMutableArray *myObject;
// A Dictionary Object
NSDictionary *dictionary;
NSString *name;
NSString *subject;
NSString *avatar;
NSString *rate;
NSString *bio;
NSString *nick;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//[self fetch_tutors]; // Get tutors Ordered By Karma DESC
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
/*
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 0;
} */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return myObject.count; // This is old 100% working code
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
dictionary = [myObject objectAtIndex:indexPath.row];
cell.labelName.text = [dictionary objectForKey:#"name"];
cell.labelBio.text = [dictionary objectForKey:#"bio"];
cell.labelSubjects.text = [dictionary objectForKey:#"subject"];
cell.imageAvatar.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[dictionary objectForKey:#"avatar"]]]];
cell.imageAvatar.clipsToBounds = YES;
cell.imageAvatar.layer.cornerRadius = cell.imageAvatar.frame.size.width / 2;
cell.labelRate.text = [dictionary objectForKey:#"rate"];
cell.labelRate.layer.cornerRadius = 5;
[SimpleAudioPlayer playFile:#"upvote.wav"];
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showUserDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UserDetailViewController *destViewController = segue.destinationViewController;
NSDictionary *dict = [myObject objectAtIndex:indexPath.row];
destViewController.http_nick = [dict valueForKey:#"nick"];
destViewController.labelName = [dict valueForKey:#"name"];
NSLog(#" Username Sent--->%#", [dict valueForKey:#"nick"]);
NSLog(#" name Sent--->%#", [dict valueForKey:#"name"]);
}
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
NSString *searchTerm = searchBar.text;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Searching Tutors";
[hud show:YES];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSString *url = [NSString stringWithFormat:#"http://api.domain.com/user-search/?subject=%#",searchTerm];
[manager GET:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"JSON: %#", responseObject);
[hud hide:YES];
int count_total = [[responseObject objectForKey:#"count"] intValue];
if (count_total == 0) {
[myObject removeAllObjects];
NSLog(#"Count is --->0");
}
else {
myObject = [[NSMutableArray alloc] init];
name = #"name";
subject = #"subject";
avatar = #"avatar";
rate = #"rate";
bio = #"bio";
nick = #"nick";
for (NSDictionary *dataDict in [responseObject objectForKey:#"results"])
{
NSString *name_data = [dataDict objectForKey:#"name"];
NSString *subject_data = [dataDict objectForKey:#"subjects"];
NSString *avatar_data = [dataDict objectForKey:#"avatar_s"];
NSString *rate_data = [dataDict objectForKey:#"user_rate"];
NSString *bio_data = [dataDict objectForKey:#"bio"];
NSString *nick_data = [dataDict objectForKey:#"nick"];
NSLog(#"name: %#", name_data);
NSLog(#"subjects: %#",subject_data);
NSLog(#"avatar_s: %#", avatar_data);
NSLog(#"user_rate: %#",rate_data);
NSLog(#"bio: %#",bio_data);
NSLog(#"nick: %#",nick_data);
dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
name_data,name,
subject_data,subject,
avatar_data,avatar,
rate_data,rate,
bio_data, bio,
nick_data, nick, nil];
[myObject addObject:dictionary];
}
}
[self.searchBar endEditing:YES];
[self.searchBar setShowsCancelButton:NO animated:YES];
[self.searchBar sizeToFit];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %#", error);
[hud hide:YES];
}];
}
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
[searchBar sizeToFit];
[searchBar setShowsCancelButton:YES animated:YES];
return YES;
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
[self.searchBar endEditing:YES];
[searchBar setShowsCancelButton:NO animated:YES];
self.searchBar.text = nil;
}
/*
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"userDetailView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UserDetailViewController *destViewController = segue.destinationViewController;
destViewController.labelName = [myObject objectAtIndex:indexPath.row];
}
} */
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the item to be re-orderable.
return YES;
}
*/
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
One way to achieve it is,
Return the numberOfCells as one more than the data array
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return myDataArray +1 ; //myDataArray is the data and One more cell for the last cell (10 out of 46 results cell)
}
In cellForRowAtIndexPath, if your IndexPath.row == myDataArray.count (ie., Last Row) Create a custom cell which looks as in the picture and return the custom cell.
In - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath, If the indexPath corresponds to last cell, then Start Request for page 2, once the results are fetched, update the myDataArray and reload the table.

Custom NSPopupButtonCell in NSTableView

I am trying to create a custom popup menu in a tableview. As I understand it I can should be able to do so by calling the [NSPopupButtonCell setView:myView] method passing in the custom view (which is just a NSView with an NSOutlineView in it).
So I have created a NSPopupButtonCell subclass and during initialisation I call setView and pass in the custom outline view..
EDIT
In IB I have set the table columns cell to a Popup Button Cell and set
the class to my custom LookupPopupButtonCell.
I still don't get my custom view displaying but my custom classes
initialisation methods appear to be getting called.
I have since replaced this approach with using a NSTableViewDelegate method dataCellForTableColumn. Now the popup shows my custom tableView.
Still no joy in getting the NSOutlineViewDelegate methods called though.
EDIT
OK I have managed to get things working using a NSPopupButton on a view. delegate works find and table view displays things fine. Seems that using NSPopupButtonCell the delegate methods never get called.
#implementation LookupPopupButtonCell
- (id)init
{
LOG(#"init called");
self = [super init];
if (self) {
[self initialise];
}
return self;
}
- (void)initialise
{
LOG(#"initialise called");
[self setFont:[NSFont fontWithName:#"System Regular" size:11]];
[self setBordered:NO];
[self setBezeled:NO];
// Set the Task Lookup Popup Menu
NSMenu *newLookupMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:#"Custom"];
NSMenuItem *newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:#"Lookup" action:nil keyEquivalent:#""];
[newItem setEnabled:YES];
TaskLookupViewController *viewController = [[TaskLookupViewController alloc] initWithNibName:#"TaskLookupViewController" bundle:nil];
[newItem setView:[viewController view]];
[newLookupMenu addItem:newItem];
[newItem release];
[self setMenu:newLookupMenu];
}
#end
#implementation TaskLookupViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialization code here.
[self initialise];
}
return self;
}
- (void)awakeFromNib{
LOG(#"awakeFromNib called...");
[self viewDidLoad];
}
- (void)viewDidLoad {
FLOG(#"viewDidLoad called for %#", self);
/*
FLOG(#" _outlineView is %#", _outlineView);
FLOG(#" _outlineView is %#", [_outlineView identifier]);
FLOG(#" _outlineView delegate is %#", [_outlineView delegate]);
FLOG(#" _outlineView dataSource is %#", [_outlineView dataSource]);
*/
[_outlineView setDataSource:self];
[_outlineView setDelegate:self];
[_outlineView reloadData];
[_outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
[_outlineView setNeedsDisplay];
[_outlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:2] byExtendingSelection:NO];
/*
FLOG(#" _outlineView delegate is %#", [_outlineView delegate]);
FLOG(#" _outlineView dataSource is %#", [_outlineView dataSource]);
*/
//NSTableColumn *tableColumn = [[_outlineView tableColumns] objectAtIndex:0];
//LOG(#" setting bindings");
//[tableColumn bind: #"value" toObject: _treeController withKeyPath: #"arrangedObjects.displayName" options: nil];
}
- (void)initialise {
LOG(#"initialise called");
_topLevelItems = [[NSArray arrayWithObjects:#"Project", #"Tasks and Deliverables", #"Finance", nil] retain];
_childrenDictionary = [NSMutableDictionary new];
[_childrenDictionary setObject:[NSArray arrayWithObjects:#"Scope", nil] forKey:#"Project"];
//[_childrenDictionary setObject:[NSArray arrayWithObjects:#"Issues", #"Risks", nil] forKey:#"Quality"];
[_childrenDictionary setObject:[NSArray arrayWithObjects:#"WBS", #"Functions", nil] forKey:#"Tasks and Deliverables"];
[_childrenDictionary setObject:[NSArray arrayWithObjects:#"Expenses", #"Ongoing Costs", #"Timesheets", nil] forKey:#"Finance"];
//[_childrenDictionary setObject:[NSArray arrayWithObjects:#"Applications", #"Interfaces", nil] forKey:#"IT Systems"];
//[_childrenDictionary setObject:[NSArray arrayWithObjects:#"People", #"Setup", nil] forKey:#"Administration"];
}
- (NSArray *)_childrenForItem:(id)item {
LOG(#"_childrenForItem called");
NSArray *children;
if (item == nil) {
children = _topLevelItems;
} else {
children = [_childrenDictionary objectForKey:item];
}
//FLOG(#" children are %#", children);
return children;
}
#end
#implementation TaskLookupViewController (NSOutlineViewDataSource)
- (void)outlineViewSelectionDidChange:(NSNotification *)notification {
LOG(#"outlineViewSelectionDidChange: called");
if ([_outlineView selectedRow] != -1) {
NSObject *item = [_outlineView itemAtRow:[_outlineView selectedRow]];
if ([_outlineView parentForItem:item] != nil) {
// Only change things for non-root items (root items can be selected, but are ignored)
FLOG(#" selected item is %#", item);
}
}
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
FLOG(#"outlineView:child:ofItem: called for item %#", item);
return [[self _childrenForItem:item] objectAtIndex:index];
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
LOG(#"outlineView:isItemExpandable: called");
if ([outlineView parentForItem:item] == nil) {
return YES;
} else {
return YES;
}
}
- (NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
LOG(#"outlineView:numberOfChildrenOfItem: called");
FLOG(#" children count is %d", [[self _childrenForItem:item] count]);
return [[self _childrenForItem:item] count];
}
#end
#implementation TaskLookupViewController (NSOutlineViewDelegate)
- (BOOL)outlineView:(NSOutlineView *)outlineView isGroupItem:(id)item {
LOG(#"outlineView:isGroupItem: called");
return [_topLevelItems containsObject:item];
}
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
LOG(#"willDisplayCell called");
[cell setTitle:#"Cell Title"];
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
LOG(#"outlineView:viewForTableColumn called");
// We just return a regular text view.
if ([_topLevelItems containsObject:item]) {
NSTextField *result = [outlineView makeViewWithIdentifier:#"HeaderTextField" owner:self];
// Uppercase the string value, but don't set anything else. NSOutlineView automatically applies attributes as necessary
NSString *value = [item uppercaseString];
[result setStringValue:value];
return result;
} else {
NSTextField *result = [outlineView makeViewWithIdentifier:#"ItemTextField" owner:self];
[result setStringValue:value];
return result;
}
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item;
{
LOG(#"outlineView:shouldExpandItem: called");
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item;
{
LOG(#"outlineView:shouldSelectItem: called");
return YES;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldCollapseItem:(id)item;
{
LOG(#"outlineView:shouldCollapseItem: called");
return NO;
}
#end
#implementation TaskLookupViewController (NSTableViewDelegate)
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
LOG(#"tableView:dataCellForTableColumn:row: called");
NSString *identifier = [tableColumn identifier];
if ([identifier isEqualToString:#"task"]) {
//LOG(#" task column setting the cell");
LookupPopupButtonCell *cellView = [[LookupPopupButtonCell alloc] init];
return cellView;
}
NSTextFieldCell *cellView = [tableView makeViewWithIdentifier:#"hoursCell" owner:self];
return cellView;
}
#end
It seems you must use a VIEW based tableView to get the delegate messages. Blah, now to figure out how to bind one of them to Core Data, hopefully its not too hard !
Is there any way to reuse the same menu for each row ? I guess as long as the dataSource is not recreated each time its probably not too bad, still there could be lots of rows in this hierarchy!

No known class method for selector 'defaultStore'

Am working my through the Big Nerd Ranch iOS Programming 3rd edition, and have run into a problem with the selector 'defaultStore' which is a singleton. The error says that there is no such class method, and I don't know how to fix the problem, which I have commented out.
I believe that there is a part where the book will switch over to Core Data, but I haven't reached that part yet.
ItemsViewController.m
#import "ItemsViewController.h"
#import "BNRItemStore.h"
#import "BNRItem.h"
#implementation ItemsViewController
- (id)init
{
// Call the superclass's designated initializer
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
UINavigationItem *n = [self navigationItem];
[n setTitle:#"Homepwner"];
// Create a new bar button item that will send
// addNewItem: to ItemsViewController
UIBarButtonItem *bbi = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addNewItem:)];
// Set this bar button item as the right item in the navigationItem
[[self navigationItem] setRightBarButtonItem:bbi];
[[self navigationItem] setLeftBarButtonItem:[self editButtonItem]];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[self tableView] reloadData];
}
- (IBAction)addNewItem:(id)sender
{
// Create a new BNRItem and add it to the store
BNRItem *newItem = [[BNRItemStore defaultStore] createItem];//No known class method for selector 'defaultStore'
// Incompatible pointer types initializing 'BNRItem*__strong' with an expression of 'NSArray'
// Figure out where that item is in the array
int lastRow = [[[BNRItemStore defaultStore] allItems] indexOfObject:newItem]; //No known class method for selector 'defaultStore'
NSIndexPath *ip = [NSIndexPath indexPathForRow:lastRow inSection:0];
// Insert this new row into the table.
[[self tableView] insertRowsAtIndexPaths:[NSArray arrayWithObject:ip]
withRowAnimation:UITableViewRowAnimationTop];
}
- (id)initWithStyle:(UITableViewStyle)style
{
return [self init];
}
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath
{
[[BNRItemStore defaultStore] moveItemAtIndex:[fromIndexPath row] //No known class method for selector 'defaultStore'
toIndex:[toIndexPath row]];
}
- (void)tableView:(UITableView *)aTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [[DetailViewController alloc] init];
NSArray *items = [[BNRItemStore defaultStore] allItems];//No known class method for selector 'defaultStore'
BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];
// Give detail view controller a pointer to the item object in row
[detailViewController setItem:selectedItem];
// Push it onto the top of the navigation controller's stack
[[self navigationController] pushViewController:detailViewController
animated:YES];
}
- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
// If the table view is asking to commit a delete command...
if (editingStyle == UITableViewCellEditingStyleDelete)
{
BNRItemStore *ps = [BNRItemStore defaultStore];//No known class method for selector 'defaultStore'
NSArray *items = [ps allItems];
BNRItem *p = [items objectAtIndex:[indexPath row]];
[ps removeItem:p];
// We also remove that row from the table view with an animation
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [[[BNRItemStore defaultStore] allItems] count];//No known class method for selector 'defaultStore'
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of UITableViewCell, with default appearance
// Check for a reusable cell first, use that if it exists
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
// If there is no reusable cell of this type, create a new one
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"UITableViewCell"];
}
// Set the text on the cell with the description of the item
// that is at the nth index of items, where n = row this cell
// will appear in on the tableview
BNRItem *p = [[[BNRItemStore defaultStore] allItems]//No known class method for selector 'defaultStore'
objectAtIndex:[indexPath row]];
[[cell textLabel] setText:[p description]];
return cell;
}
#end
BNRItemStore.h
#import "BNRItemStore.h"
#import "BNRItem.h"
#implementation BNRItemStore
+ (BNRItemStore *)defaultStore
{
static BNRItemStore *defaultStore = nil;
if(!defaultStore)
defaultStore = [[super allocWithZone:nil] init];
return defaultStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self defaultStore];
}
- (id)init
{
self = [super init];
if(self) {
allItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void)removeItem:(BNRItem *)p
{
[allItems removeObjectIdenticalTo:p];
}
- (NSArray *)allItems
{
return allItems;
}
- (void)moveItemAtIndex:(int)from
toIndex:(int)to
{
if (from == to) {
return;
}
// Get pointer to object being moved so we can re-insert it
BNRItem *p = [allItems objectAtIndex:from];
// Remove p from array
[allItems removeObjectAtIndex:from];
// Insert p in array at new location
[allItems insertObject:p atIndex:to];
}
- (BNRItem *)createItem
{
BNRItem *p = [BNRItem randomItem];
[allItems addObject:p];
return p;
}
BNRItemStore.m
#import "BNRItemStore.h"
#import "BNRItem.h"
#implementation BNRItemStore
+ (BNRItemStore *)defaultStore
{
static BNRItemStore *defaultStore = nil;
if(!defaultStore)
defaultStore = [[super allocWithZone:nil] init];
return defaultStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self defaultStore];
}
- (id)init
{
self = [super init];
if(self) {
allItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void)removeItem:(BNRItem *)p
{
[allItems removeObjectIdenticalTo:p];
}
- (NSArray *)allItems
{
return allItems;
}
- (void)moveItemAtIndex:(int)from
toIndex:(int)to
{
if (from == to) {
return;
}
// Get pointer to object being moved so we can re-insert it
BNRItem *p = [allItems objectAtIndex:from];
// Remove p from array
[allItems removeObjectAtIndex:from];
// Insert p in array at new location
[allItems insertObject:p atIndex:to];
}
- (BNRItem *)createItem
{
BNRItem *p = [BNRItem randomItem];
[allItems addObject:p];
return p;
}
#end
Make sure that the #interface block for BNRItemStore (in BNRItemStore.h) declares that method:
#interface BNRItemStore : NSObject
+ (BNRItemStore *)defaultStore;
// etc.
#end
The compiler looks at the interface to know what methods are available on that class.

Invalid argument type to unary expression

I have this program with a tableView as my first view. I have also implemented (or at least tried to) a search bar on top of the view. Have used several hours to search for a solution, but without positive results.
#import "FirstViewController.h"
#import "NSDictionary-MutableDeepCopy.h"
#implementation FirstViewController
#synthesize listData, table, search, allNames, names, keys;
#pragma mark -
#pragma mark Custom Methods
- (void)resetSearch {
NSMutableDictionary *allNamesCopy = [self.allNames mutableDeepCopy];
self.names = allNamesCopy;
[allNamesCopy release];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
[keyArray addObjectsFromArray:[[self.allNames allKeys]
sortedArrayUsingSelector:#selector(compare:)]];
self.keys = keyArray;
[keyArray release];
}
-(void)handleSearchForTerm:(NSString *)searchTerm {
NSMutableArray *sectionsToRemove = [[NSMutableArray alloc] init];
[self resetSearch];
for (NSString *key in self.keys) {
NSMutableArray *array = [names valueForKey:key];
NSMutableArray *toRemove = [[NSMutableArray alloc] init];
for (NSString *name in listData) {
if ([name rangeOfString:searchTerm
options:NSCaseInsensitiveSearch].location == NSNotFound)
[toRemove addObject:name];
}
if ([array count] == [toRemove count])
[sectionsToRemove addObject:key];
[array removeObjectsInArray:toRemove];
[toRemove release];
}
[self.keys removeObjectsInArray:sectionsToRemove];
[sectionsToRemove release];
[table reloadData];
}
- (void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"sortednames" ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.names = dict;
self.allNames = dict;
[dict release];
[self resetSearch];
[table reloadData];
[table setContentOffset:CGPointMake(0.0, 44.0)animated:NO];
self.parentViewController.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"background.png"]];
NSArray *array = [[NSArray alloc] initWithObjects:
// A larger amount of objects here.
self.listData = array;
[array release];
[super viewDidLoad];
}
/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
// Custom initialization
}
return self;
}
*/
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.listData = nil;
self.table = nil;
self.search = nil;
self.allNames = nil;
self.names = nil;
self.keys = nil;
}
- (void)dealloc {
[listData release];
[search release];
[table release];
[allNames release];
[keys release];
[names release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return ([keys count] > 0) ? [keys count] : 1;
}
- (NSInteger)tableView:(UITableView *)aTableView
numberOfRowsInSection: (NSInteger)section {
return [self.listData count];
if ([keys count] == 0)
return 0;
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
return [nameSection count];
}
- (UITableViewCell *) extracted_method: (UITableViewCell *) cell {
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)aTableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSString *key = [keys objectAtIndex:section];
NSArray *nameSection = [names objectForKey:key];
static NSString *sectionsTableIdentifier = #"sectionsTableIdentifier";
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:
sectionsTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: sectionsTableIdentifier] autorelease];
}
cell.backgroundColor = [UIColor clearColor];
cell.textColor = [UIColor whiteColor];
cell.detailTextLabel.textColor = [UIColor whiteColor];
cell.text = [nameSection objectAtIndex:row];
[self extracted_method: cell].text = [listData objectAtIndex:row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
if ([keys count] == 0)
return nil;
NSString *key = [keys objectAtIndex:section];
return key;
}
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
}
#pragma mark -
#pragma mark Table View Delegate Methods
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[search resignFirstResponder];
return indexPath;
}
#pragma mark -
#pragma mark Search Bar Delegate Methods
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
NSString *searchTerm = [searchBar text];
[self handleSearchForTerm:searchTerm];
}
- (void)searchBar:(UISearchBar *)searchBar
textDidChange:(NSString *)searchTerm {
if ([searchTerm length] == 0) {
[self resetSearch];
[table reloadData];
return;
}
[self handleSearchForTerm:searchTerm];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
search.text = #"";
[self resetSearch];
[table reloadData];
[searchBar resignFirstResponder];
}
#end
Ok guys. My problem is that this doesnt get the search function to work. In addition I receive siginal SIGABRT at this line:
NSString *key = [keys objectAtIndex:section];
So I need help with two things:
1: I need to get that SIGABRT away.
Error log message: * Terminating app due to uncaught exception
'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]:
index 0 beyond bounds for empty array'
That is I don't store any data in keys. how would I do that? and what would I store?
2: Want the search function to search in my listData array!
Thanks in advance - hope u can help!
You have not finished your sectionIndexTitlesForTableView: method. Right now it is:
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
There is no closing }, so the compiler thinks everything after that is still part of that method. When you try to define the next method, use use - (NSIndexPath *) to indicate that it is an instance method which returns NSIndexPath*, but the compiler thinks you are trying to subtract something.
The solution is simple: Add the } to the end of sectionIndexTitlesForTableView:.
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return keys;
}