Drag Drop delegate for NSView could not set an attribute - cocoa

I am using NSView delegate to read the dragged excel values. For this I have subclassed NSView. My code is like-
#interface SSDragDropView : NSView
{
NSString *textToDisplay;
}
#property(nonatomic,retain) NSString *textToDisplay; // setters/getters
#synthesize textToDisplay;// setters/getters
#implementation SSDragDropView
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
[self setNeedsDisplay: YES];
return NSDragOperationGeneric;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender{
[self setNeedsDisplay: YES];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
[self setNeedsDisplay: YES];
return YES;
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender {
NSArray *draggedFilenames = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
if ([[[draggedFilenames objectAtIndex:0] pathExtension] isEqual:#"xls"]){
return YES;
} else {
return NO;
}
}
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{
NSArray *draggedFilenames = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
NSURL *url = [NSURL fileURLWithPath:[draggedFilenames objectAtIndex:0]];
NSString *textDataFile = [NSString stringWithContentsOfURL:url usedEncoding:nil error:nil]; //This text is the original excel text and its getting displayed.
[self setTextToDisplay:textDataFile];
}
I am setting the textDataFile value to a string attribute of that class. Now I am using SSDragDropView attribute value in some other class like-
SSDragDropView *dragView = [SSDragDropView new];
NSLog(#"DragView Value is %#",[dragView textToDisplay]);
But I am getting null each time. Is it like I can not set an attribute value in those delegate methods?

The above problem can be resolved just by declaring a global variable in your SSDragDropView.h class.
#import <Cocoa/Cocoa.h>
NSString *myTextToDisplay;
#interface SSDragDropView : NSView
{
The same can be set inside the desired delegate method
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender {
// .... //Your Code
NSString *textDataFile = [NSString stringWithContentsOfURL:url usedEncoding:nil error:nil];
myTextToDisplay = textDataFile;
// .... //Your Code
}
:)

Add
[dragView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
NSPasteboard *pboard = [sender draggingPasteboard];
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
NSLog(#"%#",paths);
[self setNeedsDisplay: YES];
return NSDragOperationGeneric;
}
Below code will print nil because you are not dragging anything on NSView.
SSDragDropView *dragView = [SSDragDropView new];
NSLog(#"DragView Value is %#",[dragView textToDisplay]);

Related

Cocoa app scrolling slow when window on focus, fast when not

I have a cocoa app with a window containing an NSTableView. Each row has a few columns, three radio boxes and two buttons. Currently the table has 260 rows and when the window is focused the scrolling in the table view is atrociously slow and jittery. When the window is not focused and I mouse over the table view and scroll it's buttery smooth.
I've tried to solve the slow performance by changing the background drawing and enabling CoreAnimation Layer to no avail.
Why would the scrolling be fast when the window isn't focused but slow when it is?
I'm just baffled as to why the scrolling is so darn slow.
Here's my ProposalTableViewController.h
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
#import <QuickLook/QuickLook.h>
#import <Quartz/Quartz.h>
#interface ProposalTableViewController : NSObject<NSTableViewDataSource, NSTableViewDelegate, QLPreviewPanelDelegate, QLPreviewPanelDataSource>{
#public
NSMutableArray *list;
IBOutlet NSTableView *tableView;
IBOutlet NSSearchField *searchText;
IBOutlet NSTextField *countTextField;
}
#property (strong) QLPreviewPanel *previewPanel;
+ (ProposalTableViewController *)getInstance;
- (IBAction)deleteRow:(id)sender;
- (IBAction)exportData:(id)sender;
- (void)loadData;
- (void)countItems;
#end
And my ProposalTableViewController.m
#import "ProposalTableViewController.h"
#import "Proposal.h"
#import "StatusRadioView.h"
#import "DBManager.h"
#import "filePathButtonView.h"
#import <Quartz/Quartz.h>
#import "AppDelegate.h"
#implementation Proposal (QLPreviewItem)
- (NSURL *)previewItemURL
{
return [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#.pdf",self.filePath]];
}
- (NSString *)previewItemTitle
{
return [NSString stringWithFormat:#"Proposal %#",self.proposalNumber];
}
#end
#implementation ProposalTableViewController
static ProposalTableViewController *instance;
+ (ProposalTableViewController *)getInstance{
return instance;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Init
//------------------------------------------------------------------------------------------------------------------------------------
- (id)init{
self = [super init];
if(self){
instance = self;
list = [[NSMutableArray alloc] init];
[self loadData];
for (NSTableColumn *tableColumn in tableView.tableColumns ) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:tableColumn.identifier ascending:YES selector:#selector(compare:)];
[tableColumn setSortDescriptorPrototype:sortDescriptor];
}
}
return self;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Load Data from SQLite
//------------------------------------------------------------------------------------------------------------------------------------
- (void)loadData {
list = [[DBManager getProposalTable] select:#""];
NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:#"proposalNumber" ascending:NO selector:#selector(compare:)];
[list sortUsingDescriptors:[NSArray arrayWithObjects:desc, nil]];
[tableView reloadData];
[self countItems: list];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Number of Rows in Table View
//------------------------------------------------------------------------------------------------------------------------------------
- (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView {
return [list count];
}
-(void)countItems:(NSArray *)list; {
NSString *countText;
int size = [list count];
if (size == 1){
countText = #"item in list";
}else{
countText = #"items in list";
}
NSString *itemCount = [NSString stringWithFormat:#"%d %#", size, countText];
[countTextField setStringValue:itemCount];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Get View for Table Column
//------------------------------------------------------------------------------------------------------------------------------------
- (NSView *)tableView:(NSTableView *)table_view viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
Proposal *p = [list objectAtIndex:row];
NSString *identifier = [tableColumn identifier];
NSString *holdingValue;
NSTableCellView *cell = [table_view makeViewWithIdentifier:identifier owner:self];
if([identifier isEqualToString:#"status"]){
StatusRadioView *radioView = [[StatusRadioView alloc] initWithProposal:p];
return radioView;
}else if ([identifier isEqualToString:#"filePath"]){
filePathButtonView *buttonView = [[filePathButtonView alloc]initWithProposal:p];
return buttonView;
}else if ([identifier isEqualToString:#"clientAccessPoint"]){
holdingValue = [p valueForKey:identifier];
if (!holdingValue){
cell.textField.stringValue = #"N/A";
}else{
cell.textField.stringValue = [p valueForKey:identifier];
}
}else{
cell.textField.stringValue = [p valueForKey:identifier];
}
return cell;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Sort Descriptors did Change
//------------------------------------------------------------------------------------------------------------------------------------
-(void)tableView:(NSTableView *)mtableView sortDescriptorsDidChange:(NSArray *)oldDescriptors {
[list sortUsingDescriptors: [mtableView sortDescriptors]];
[tableView reloadData];
}
//table row height
-(CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row {
return 25;
}
//------------------------------------------------------------------------------------------------------------------------------------
// Search Text did Change
//------------------------------------------------------------------------------------------------------------------------------------
- (void)controlTextDidChange:(NSNotification *)notification {
NSSortDescriptor* desc = [[NSSortDescriptor alloc] initWithKey:#"proposalNumber" ascending:NO selector:#selector(compare:)];
NSTextField *textField = [notification object];
NSString *str = [textField stringValue];
list = [[DBManager getProposalTable] select:str];
[list sortUsingDescriptors:[NSArray arrayWithObjects:desc, nil]];
[tableView reloadData];
[self countItems: list];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Export DATA
//------------------------------------------------------------------------------------------------------------------------------------
- (IBAction)exportData:(id)sender{
NSString *content = #"";
for(Proposal *p in list){
NSString *row = [NSString stringWithFormat:#"%#,%#,%#,%#,%#,%#,%#,%#,%#,%#", p.proposalNumber,p.itemNumber,p.clientName,p.medium,p.support,p.cost,p.dateCreated,p.status,p.dateStatusChanged,p.clientAccessPoint];
row = [row stringByReplacingOccurrencesOfString:#"\n" withString:#""];
row = [NSString stringWithFormat:#"%#\n", row];
content = [content stringByAppendingString:row];
}
//get the documents directory:
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Baumgartner Fine Art Restoration"];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"proposalBuilderDatabase.csv"];
[[NSFileManager defaultManager] createDirectoryAtPath:documentsDirectory
withIntermediateDirectories:NO
attributes:nil
error:nil];
[content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Export Succeeded"];
[alert addButtonWithTitle:#"Ok"];
[alert runModal];
}
//------------------------------------------------------------------------------------------------------------------------------------
// Delete row from DB and table
//------------------------------------------------------------------------------------------------------------------------------------
- (IBAction)deleteRow:(id)sender{
NSString *row = list[tableView.selectedRow];
NSString *mid = [row valueForKey:#"m_id"];
ProposalTable *deleteRow = [[ProposalTable alloc] init];
[deleteRow deleteWithId: mid];
[self loadData];
[tableView reloadData];
}
//------------------------------------------------------------------------------------------------------------------------------------
// QuickLook
//------------------------------------------------------------------------------------------------------------------------------------
- (NSInteger)numberOfPreviewItemsInPreviewPanel:(QLPreviewPanel *)panel {
return [list count];
}
- (id <QLPreviewItem>)previewPanel:(QLPreviewPanel *)panel previewItemAtIndex:(NSInteger)index {
return list[tableView.selectedRow];
}
- (BOOL)previewPanel:(QLPreviewPanel *)panel handleEvent:(NSEvent *)event {
// redirect all key down events to the table view
if ([event type] == NSKeyDown) {
[tableView keyDown:event];
return YES;
}
return NO;
}
// This delegate method provides the rect on screen from which the panel will zoom.
- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item {
return NSZeroRect;
}
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)rowIndex {
[[QLPreviewPanel sharedPreviewPanel]reloadData];
return YES;
}
#end
Here's my ProposalTableView.h
#import <Cocoa/Cocoa.h>
#interface ProposalTableView : NSTableView
#end
And my ProposalTableView.m
#import "ProposalTableView.h"
#import "AppDelegate.h"
#implementation ProposalTableView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
self.wantsLayer = YES;
// Drawing code here.
}
- (void)keyDown:(NSEvent *)theEvent
{
NSString *key = [theEvent charactersIgnoringModifiers];
if ([key isEqual:#" "])
{
[[NSApp delegate] togglePreviewPanel:self];
}
else
{
[super keyDown:theEvent];
}
}
#end
There's also code that establishes the SqLite DB connection and interacts with the DB to get the records or delete etc... but that's not really needed here... I also have code that draws the radio buttons and the other buttons but again, I don't think that's necessary unless someone thinks that the drawing is creating the slowdown...?
So it turns out that the drawing of the radio buttons is what is causing the slowdown... back to the drawing board.

Saving data to an existing plist

I am working on a project that has a simple tableview with detail view.
Data source is a plist. I am trying to allow user input to be saved into the plist and shown in tableview. i have created a add view controller which gets presented and dismissed modally and has two text fields which allow the user to add the name of the city and name of the states, also a text field to input description.
Problem: how to save this data to my existing plist and show it in the tableview. Here is my code for the table view:
#implementation TableViewController
#synthesize content, searchResults;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
content = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"]];
}
- (IBAction)add;
{
AddViewController* controller = [[AddViewController alloc] init];
[self presentViewController:controller animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [self.searchResults count];
} else {
return [self.content count];
}
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat: #"SELF['city'] BEGINSWITH[c] %# ", searchText];
searchResults = [[content filteredArrayUsingPredicate:resultPredicate] retain];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];
return YES;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"city"];
cell.detailTextLabel.text = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"state"];
cell.imageView.image = [UIImage imageNamed:[[self.searchResults objectAtIndex:indexPath.row] valueForKey:#"cityImage"]];
} else {
cell.textLabel.text = [[self.content objectAtIndex:indexPath.row] valueForKey:#"city"];
cell.detailTextLabel.text = [[self.content objectAtIndex:indexPath.row] valueForKey:#"state"];
cell.imageView.image = [UIImage imageNamed:[[self.content objectAtIndex:indexPath.row] valueForKey:#"cityImage"]];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
[self performSegueWithIdentifier: #"showDetails" sender: self];
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showDetails"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *DVC = [segue destinationViewController];
if ([self.searchDisplayController isActive]) {
DVC.cityImageString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"cityImage"];
DVC.cityTextString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"cityText"];
DVC.cityNameString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"city"];
DVC.stateNameString = [[searchResults objectAtIndex:indexPath.row] valueForKey:#"state"];
} else {
DVC.cityImageString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"cityImage"];
DVC.cityTextString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"cityText"];
DVC.cityNameString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"city"];
DVC.stateNameString = [[self.content objectAtIndex:indexPath.row] valueForKey:#"state"];
}
}
}
and here is the code for addViewController.h:
#interface AddViewController : UIViewController <UINavigationControllerDelegate,UIImagePickerControllerDelegate>{
IBOutlet UITextField *cityTextField;
IBOutlet UITextField *stateTextField;
IBOutlet UITextView *cityDescription;
UIImagePickerController* imagePicker;
}
#property (nonatomic, copy) NSString* name;
#property (nonatomic, copy) NSString* description;
#property (nonatomic, strong) UIImage* image;
#property (nonatomic, retain) IBOutlet UINavigationBar* navigationBar;
#property (nonatomic, strong) UITextField *cityTextField;
#property (nonatomic, strong) UITextField *stateTextField;
#property (nonatomic, strong) UITextView *cityDescription;
#property (nonatomic, strong) IBOutlet UIButton* choosePhotoButton;
#property (nonatomic, strong) IBOutlet UIButton* takePhotoButton;
- (IBAction)save;
- (IBAction)cancel;
- (IBAction)choosePhoto;
- (IBAction)takePhoto;
#end
and at last the add view controller .m
#implementation AddViewController
#synthesize cityTextField, stateTextField, cityDescription;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (IBAction)save
{
// Make sure the user has entered at least a recipe name
if (self.cityTextField.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter a city name"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
if (self.stateTextField.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter a city name"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
// Make sure the user has entered at least a recipe name
if (self.cityDescription.text.length == 0)
{
UIAlertView* alertView = [[UIAlertView alloc]
initWithTitle:#"Whoops..."
message:#"Please enter city description"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
return;
}
self.name = self.cityTextField.text;
self.name = self.stateTextField.text;
self.description = self.cityDescription.text;
if ([[self parentViewController] respondsToSelector:#selector(dismissViewControllerAnimated:)]){
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
}
- (IBAction)cancel {
{
if ([[self parentViewController] respondsToSelector:#selector(dismissModalViewControllerAnimated:)]){
[[self parentViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
}
}
- (IBAction)choosePhoto
{
// Show the image picker with the photo library
imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePicker.delegate = self;
imagePicker.allowsEditing = YES;
[self presentViewController:imagePicker animated:YES completion:nil];
}
- (IBAction)takePhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
//picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([cityDescription isFirstResponder] && [touch view] != cityDescription) {
[cityDescription resignFirstResponder];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[cityTextField resignFirstResponder];
[stateTextField resignFirstResponder];
}
#pragma mark -
#pragma mark UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
// We get here when the user has successfully picked an image.
// Put the image in our property and set it on the button.
if (imagePicker) {
self.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self.choosePhotoButton setImage:self.image forState:UIControlStateNormal];
} else {
if (picker) {
self.image = [info objectForKey:UIImagePickerControllerEditedImage];
[self.takePhotoButton setImage:self.image forState:UIControlStateNormal];
}
}
[self dismissViewControllerAnimated:YES completion:nil];
[imagePicker release];
imagePicker = nil;
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker
{
[self dismissViewControllerAnimated:YES completion:nil];
[imagePicker release];
imagePicker = nil;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
i know there are tons of almost similar questions and believe me i have looked at so many of them and i think i am hitting the wall with this one and i cant think of a proper way to do this. it may be so simple but for the life of me i cant figure this out and thats why i am trying to ask for help. i truly appreciate any help i can get. if needs be i can place the sample project on git hub for ease of access. also the project is built in ios 6 and the latest Xcode.
P.S. here is the link to the project on git hub: https://github.com/AdrianPhillips/TableSearch
I can't find your code that do the actual "save" work. I guess it should be in the UIAlertView delegate,right ? And you did not tell us what's your problem. Following code maybe what's you seeking.
[content writeToFile:filePath atomically:YES]
Another reminder is: you should NOT save the plist back to main bundle, save it to Documents or Cache or other folder.
Just as every one has pointed out you can not write to the plist that is in your main bundle. You need to copy it from the bundle to your application document directory. Then you can just init from and write to the path in your documents directory. The other issue you have, is that nothing is communicating the new data from the AddViewController back to the TableViewController. You should create a protocol and delegate for your AddViewController.
For all the gore details see my pull request on GitHub.
https://github.com/GayleDDS/TableSearch.git
You have to copy the plist to the Documents folder first. You can't edit it into the original app folder. You can use this class to do this:
- (void) CreatePlistCopyInDocuments:(NSString *)plistName {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:plistName];
success = [fileManager fileExistsAtPath:writablePath];
if (success) {
return;
}
// The writable file does not exist, so copy from the bundle to the appropriate location.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:plistName];
success = [fileManager copyItemAtPath:defaultPath toPath:writablePath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable file with message '%#'.", [error localizedDescription]);
}
}
You have just to pass the plist name to it.
Then you have to load it into some NSMutableDictionary like this:
NSString *documents = [NSHomeDirectory() stringByAppendingString:#"/Documents"];
NSString *plistInDocuments = [documents stringByAppendingPathComponent:#"UserData.plist"];
NSMutableDictionary *plistData = [[NSMutableDictionary alloc]initWithContentsOfFile:plistInDocuments];
After you have updated the values for the dictionary, you have to write the file back to the documents like this:
[plistData writeToFile:plistInDocuments atomically:YES];

Drag & drop (<NSDraggingDestination>) in Cocoa does not work

I want to implement some "simple" drag & drop into my Mac application.
I dragged in a view and entered "MyView" in my nib file accordingly.However I do not get any response in my console (I try to log messages whenever any of the protocol methods are triggered)
I have subclassed NSView like this:
#interface MyView : NSView <NSDraggingDestination>{
NSImageView* imageView;
}
and implement it like this:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self registerForDraggedTypes:[NSImage imagePasteboardTypes]];
NSRect rect = NSMakeRect(150, 0, 400, 300);
imageView = [[NSImageView alloc] initWithFrame:rect];
[imageView setImageScaling:NSScaleNone];
[imageView setImage:[NSImage imageNamed:#"test.png"]];
[self addSubview:imageView];
}
return self;
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
NSLog(#"entered");
return NSDragOperationCopy;
}
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender{
return NSDragOperationCopy;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender{
NSLog(#"exited");
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender{
NSLog(#"som");
return YES;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSPasteboard *pboard = [sender draggingPasteboard];
if ( [[pboard types] containsObject:NSURLPboardType] ) {
NSURL *fileURL = [NSURL URLFromPasteboard:pboard];
NSLog(#"%#", fileURL);
}
return YES;
}
- (void)concludeDragOperation:(id <NSDraggingInfo>)sender{
NSLog(#"conclude sth");
}
- (void)draggingEnded:(id <NSDraggingInfo>)sender{
NSLog(#"ended");
}
- (BOOL)wantsPeriodicDraggingUpdates{
NSLog(#"wants updates");
}
- (void)updateDraggingItemsForDrag:(id <NSDraggingInfo>)sender NS_AVAILABLE_MAC(10_7){
}
If anyone still needs this, using:
[self registerForDraggedTypes:[NSArray arrayWithObjects:
NSFilenamesPboardType, nil]];
instead of:
[self registerForDraggedTypes:
[NSImage imagePasteboardTypes]]; // BAD: dropped image runs away, no draggingdestination protocol methods sent...
solved this problem for me. I suspect this works because my XIB is targeting osX 10.5. NSFilenamesPboardType represents a 10.5-and-previous 'standard data' UTI and [NSImage imagePasteboardTypes] returns 10.6-and-after standard data UTIs.
By the way, in - (BOOL)performDragOperation:(id NSDraggingInfo)sender, you can get a path for the file that was dropped with:
NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
Got the solution here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DragandDrop/Tasks/acceptingdrags.html#//apple_ref/doc/uid/20000993-BABHHIHC

How to call registerForDraggedTypes in NSViewController?

I have a MainViewController with a splitview in it. Next I have two viewcontroller to control each of the views in the splitview. I want to be able to drag and drop a file in one of those views.
But I can't seem to get the dragging to work? There's no "plus-sign" on the file when dragged to the view and dropping it doesn't do anything either.
What am I doing wrong?
First here's MainViewController.m
fileViewController = [[FileViewController alloc] initWithNibName:#"FileViewController" bundle:nil];
terminalViewController = [[TerminalViewController alloc] initWithNibName:#"TerminalViewController" bundle:nil];
[splitView replaceSubview:[[splitView subviews] objectAtIndex:0] with:[fileViewController view]];
[splitView replaceSubview:[[splitView subviews] objectAtIndex:1] with:[terminalViewController view]];
Next, my code to handle the dragging in the FileViewController
#dynamic isHighlighted;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
NSLog(#"registering");
[self.view registerForDraggedTypes:[NSArray arrayWithObjects: NSFilenamesPboardType, nil]];
}
return self;
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSLog(#"[%# %#]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
NSPasteboard *pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
for (NSString *path in paths) {
NSError *error = nil;
NSString *utiType = [[NSWorkspace sharedWorkspace]
typeOfFile:path error:&error];
if (![[NSWorkspace sharedWorkspace]
type:utiType conformsToType:(id)kUTTypeFolder]) {
[self setHighlighted:NO];
return NSDragOperationNone;
}
}
}
[self setHighlighted:YES];
return NSDragOperationEvery;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender {
[self setHighlighted:NO];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
return YES;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
[self setHighlighted:NO];
return YES;
}
- (void)concludeDragOperation:(id )sender {
[self.view setNeedsDisplay:YES];
} // end concludeDragOperation
- (BOOL)isHighlighted {
return isHighlighted;
}
- (void)setHighlighted:(BOOL)value {
isHighlighted = value;
[self.view setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)frame {
[self.view drawRect:frame];
if (isHighlighted) {
[NSBezierPath setDefaultLineWidth:6.0];
[[NSColor keyboardFocusIndicatorColor] set];
[NSBezierPath strokeRect:frame];
}
}
I didn't seem to fix it, and because no one gave me any answers, I fixed it by making an NSView subclass and adding it to my viewcontroller like this.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
draggable = [[DragView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)];
[[self view] addSubview:draggable];
}
return self;
}
I've never done OS X programming, but the problem is that since you're registering self.view to receive drag and drop, your self.view should handle the drag and drop operations and not your view controller. So you're correct in subclassing NSView to DragView, and you should pass the drag operations back to the view controller.
Also, I believe - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender should be implemented.

Drag and Drop folder view cocoa

I need to make a drag and drop view in cocoa that will accept folders. I know it will use things like NSView and probably registerForDraggedTypes: (which I still am not sure how to go about using). Does anyone know how to get this working?
Thanks in advance
Make a class called DragDropView that subclasses NSView and set the view in MainMenu.xib to be of this type (Select your view, go to Identity Inspecor and write DragDropView in Custom Class).
Write the code (see below) for DragDropView and Run it. An empty window should appear.
Drag some folders onto your window. You should get the paths of the folders written in your console. Something like.
2014-02-01 11:18:10.435 Start[41767:303] (
"/Users/bob/Desktop/Heathers Animations",
"/Users/bob/Desktop/bird.atlas"
)
// DragDropView.h
#import <Cocoa/Cocoa.h>
#interface DragDropView : NSView
#end
// DragDropView.m
#import "DragDropView.h"
#implementation DragDropView {
BOOL isHighlighted;
}
- (void)awakeFromNib {
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
}
- (BOOL)isHighlighted {
return isHighlighted;
}
- (void)setHighlighted:(BOOL)value {
isHighlighted = value;
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)frame {
[super drawRect:frame];
if (isHighlighted) {
[NSBezierPath setDefaultLineWidth:6.0];
[[NSColor keyboardFocusIndicatorColor] set];
[NSBezierPath strokeRect:frame];
}
}
#pragma mark - Dragging
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
NSPasteboard *pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
NSArray *paths = [pboard propertyListForType:NSFilenamesPboardType];
for (NSString *path in paths) {
NSError *error = nil;
NSString *utiType = [[NSWorkspace sharedWorkspace]
typeOfFile:path error:&error];
if (![[NSWorkspace sharedWorkspace]
type:utiType conformsToType:(id)kUTTypeFolder]) {
[self setHighlighted:NO];
return NSDragOperationNone;
}
}
}
[self setHighlighted:YES];
return NSDragOperationEvery;
}
- (void)draggingExited:(id <NSDraggingInfo>)sender {
[self setHighlighted:NO];
}
- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender {
return YES;
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
[self setHighlighted:NO];
return YES;
}
- (void)concludeDragOperation:(id<NSDraggingInfo>)sender {
NSArray *files = [[sender draggingPasteboard] propertyListForType:NSFilenamesPboardType];
NSLog(#"%#", files);
}
#end
Most of what you need is in the drag and drop documentation, but what you need specifically is the NSFilenamesPboardType. It's an array if file paths.

Resources