I have a string called text, in the main menu xib file I linked the copy menu item with an action called through an observer.I verified with a breakpoint that this method is actually called, but the problem is that the string isn't really copied to the pasteboard:
- (void) copy: (NSNotification*) notification
{
if([[self window]isKeyWindow])
{
// It always enters in this block
NSPasteboard* pb=[NSPasteboard generalPasteboard];
NSPasteboardItem* item=[[NSPasteboardItem alloc]init];
[pb clearContents];
[item setData: [NSKeyedArchiver archivedDataWithRootObject: text] forType: NSPasteboardTypeString];
[pb writeObjects: [NSArray arrayWithObject: item]];
}
}
After entering in the block it clear all the contents of the pasteboard.But if I try to paste the copied content to text edit, it doesn't paste anything (an empty string), but the string is not of length zero.
I also tried to check the return value of writeObjects, and it returns YES.
You can simply use:
[pb setString:text forType:NSPasteboardTypeString];
Alternatively you probably want to use setString:forType on NSPasteboardItem.
Related
When copying a URL from chrome on OSX and pasting into an editable WebKit webview, nothing gets pasted.
I verified that there are items on the NSPasteboard and that the NSPasteboardItem has the following types:
"dyn.ah62d4rv4gu8zs3pcnzme2641rf4guzdmsv0gn64uqm10c6xenv61a3k",
"dyn.ah62d4rv4gu8yc6durvwwaznwmuuha2pxsvw0e55bsmwca7d3sbwu",
"public.utf8-plain-text",
"dyn.ah62d4rv4gu8yg55wqzkgc65yqzvg82pwqvdg22p0r73fk8puqyuda8b1gy5xerwdgk2a",
"dyn.ah62d4rv4gu8yg55wqzkgc65yqzvg82pwqvdg22p0r73fk8puqyuda8b1gy5xerwdg3cu"
I understand that these are auto-generated and map to WebURLsWithTitlesPboardType.
On performing the same operation from safari to webview, it works since it only contains
"public.utf8-plain-text"
Is there a known workaround for handling these UTIs better?
Webkit webviews don't seem to support paste operations for dynamic UTIs. I worked around it by recreating the pasteboard items without those UTIs when a paste: was intercepted in webview:doCommandBySelector:
- (void)cleanupPasteboard:(NSPasteboard *)pasteboard {
NSMutableArray *newItems = [[NSMutableArray alloc] init];
for (NSPasteboardItem *item in pasteboard.pasteboardItems) {
NSPasteboardItem *newItem = [[NSPasteboardItem alloc] init];
for (NSString *type in item.types) {
if (![type hasPrefix:#"dyn"]) {
[newItem setData:[item dataForType:type] forType:type];
}
}
[newItems addObject:newItem];
}
[pasteboard clearContents];
[pasteboard writeObjects:newItems];
}
I have a array that reads a .txt file and when you click a button the label changes in one of the words of the .txt file, but the label doen't change.
This is the code:
if(sender == self.button) {
NSArray *words = [[NSArray alloc] initWithObjects:#"words.txt", nil];
[randomLabel setText:[words objectAtIndex:random() % [words count]]];
}
What should I do so the label changes when I press the button?
What file do I use?
A few things here:
Reading in file into an array
Well, for starters you're not reading in the contents of the .txt file.
NSArray *words = [[NSArray alloc] initWithObjects:#"words.txt", nil];
This creates a 1 element array, with that one element being #"words.txt". I don't know the format of your .txt file, so I can't say for sure how you have to load it in. See How do I format a text file to be read in using arrayWithContentsOfFile on how to potentially do this.
Setting button text
Also, you need to make sure randomLabel actually refers to the label contained within the button, otherwise the button text won't change. Typically for a button, you'd change the title using the method:
- (void)setTitle:(NSString *)title forState:(UIControlState)state
So in your instance, it'd be:
NSString* newTitle = [words objectAtIndex:random() % [words count]];
[self.button setTitle:newTitle forState:UIControlStateNormal];
Is the code actually being called?
Double check that sender == self.button evaluates to true (for readability and clarity, I'd use [sender isEqual:self.button]). Use the debugger to step through the code, to see if that particular piece of code is being called. See http://mobile.tutsplus.com/tutorials/iphone/xcode-debugging_iphone-sdk/ on how to achieve this.
You should try using
(id)initWithContentsOfFile:(NSString *)aPath
I'm able to drag a file URL from a NSTableView cell and drop it in any text editor. According to my understanding of Apple's documentation though, when I drop the URL, the file should be copied to the drop location.
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard {
//for now putting specific file path in just to get it working
NSString *filePath = #"/Users/Jackh/Desktop/Apple.png";
[pboard declareTypes:[NSArray arrayWithObject:NSURLPboardType] owner:self];
[[NSURL URLWithString:filePath] writeToPasteboard:pboard];
return YES;
}
How do I get the file to copy from the filePath to the dropped location? It's just moving filePath as text for now.
Any ideas?
Edit: I am now using this code
-(void)awakeFromNib {
[self.tableView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:NO];
}
...
[self.tableView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, NSFileContentsPboardType, nil]];
...
- (BOOL)tableView:(NSTableView *)tv writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard {
//for now putting specific file path in just to get it working
NSString *filePath = #"/Users/Jackh/Desktop/Apple.png";
[pboard declareTypes:[NSArray arrayWithObject:NSFileContentsPboardType] owner:nil];
[pboard writeFileContents:filePath];
}
If you want to force the green "+" to copy, just use NSDragOperationCopy (only) in the operation mask, disallowing other operations; e.g. in awakeFromNib:
[self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
Here is the best way I could find to make your example work with the Finder:
- (BOOL)
tableView:(NSTableView *)tv
writeRowsWithIndexes:(NSIndexSet*)rowIndexes
toPasteboard:(NSPasteboard*)pboard {
NSString *filePath = #"/Users/kevin/Desktop/1.png";
[pboard declareTypes:[NSArray arrayWithObject:NSFilenamesPboardType]
owner:nil];
[pboard setPropertyList:[NSArray arrayWithObject:filePath]
forType:NSFilenamesPboardType];
return YES;
}
Note that Apple recommends using more modern APIs but I have found in cases like this they have a big side effect: they seem to cause files to be copied twice because URLs are magically copied in multiple forms. Having said that, your original URL example didn't work because you should have used writeObjects: on the pasteboard instead of asking the URL to writeToPasteboard:. An example with URLs:
- (BOOL)
tableView:(NSTableView *)tv
writeRowsWithIndexes:(NSIndexSet*)rowIndexes
toPasteboard:(NSPasteboard*)pboard {
NSString *filePath = #"/Users/kevin/Desktop/1.png";
[pboard declareTypes:[NSArray arrayWithObject:NSURLPboardType]
owner:nil];
[pboard writeObjects:[NSArray arrayWithObject:
[NSURL fileURLWithPath:filePath]]];
return YES;
}
As I noted in other comments, if your drag is targeting a document instead of a file manager such as the Finder then it's better to include the actual data for the file. Raw data can't be misinterpreted by a document (i.e. the document has to insert the data directly, it can't choose to insert a path string instead). On the other hand, raw-data drags don't create files in the Finder for some reason so they're useful mainly as something to add to the pasteboard as an alternative.
Here is a way to use a UTI to declare the file type and call NSData to read a file (I tried this and it works, e.g. I can drag a table view row into a Rich Text document in TextEdit and see an image inserted into the window):
- (BOOL)
tableView:(NSTableView *)tv
writeRowsWithIndexes:(NSIndexSet*)rowIndexes
toPasteboard:(NSPasteboard*)pboard {
NSString *filePath = #"/Users/kevin/Desktop/1.png";
[pboard declareTypes:[NSArray arrayWithObject:#"public.png"]
owner:nil];
[pboard setData:[NSData dataWithContentsOfFile:filePath]
forType:#"public.png"];
return YES;
}
(I don't know exactly why your writeFileContents: doesn't work but the above does work and is basically the same thing.)
I am trying to create an array of all images from the saved photo album that match a certain criteria. Here is a simplified code for it. I add the photos to myImages array and confirmed via the "Added Image" log that the right images get logged. However the array returned by the function is always empty. Fairly new to Objective-C so any suggestions would be helpful.
NSMutableArray * myImages = [NSMutableArray array];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Enumerate just the photos by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
// Within the group enumeration block, filter to enumerate just photos.
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
[group enumerateAssetsUsingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
// The end of the enumeration is signaled by asset == nil.
if (alAsset) {
ALAssetRepresentation *representation = [alAsset defaultRepresentation];
UIImage *latestPhoto = [UIImage imageWithCGImage:[representation fullResolutionImage]];
NSLog(#"Added Image");
[myImages addObject:latestPhoto];
}
}];
}
failureBlock: ^(NSError *error) {
// Typically you should handle an error more gracefully than this.
NSLog(#"No groups");
}];
return myImages;
What is imagesTakenOnDate? Is that supposed to be myImages? If so, you cannot return it in this manner as the block code will execute after the method returns. The method is asynchronous. Rather than "return" you have 2 options to be able to access the modified array outside the function:
option 1: make your method take a completion block as a parameter, and then call the completion block inside the enumerateGroupsWithTypes block, and pass the completion block the array. For example:
typedef void (^CompletionBlock)(id, NSError*);
-(void)myMethodWithCompletionBlock:(CompletionBlock)completionBlock;
then when you're done with success call:
completionBlock(myImages, nil);
and in the failureBlock call:
completionBlock(nil, error);
option 2: make the array an ivar that is retained on your parent object, rather than a local variable, and then declare it as a __block variable so it can be modified within the block.
First thing. Do you really return imagesTakenOnDate? can`t see any reference to this ivar in your code. I would say that your put some breakpoints in your code. In the gdb debugger console you can type:
po myImages
than the debugger will print out the content of your array. Hope that helps
I have an NSTableView that lists tags that are stored using Core Data. The default value for a tag is 'untitled' and I need each tag to be unique, so I have a validation routine that traps empty and non-unique values and that works fine. I don't want the user to be able to store the 'untitled' value for a tag, so I am observing the NSControlTextDidEndEditingNotification, which calls the following code:
- (void)textEndedEditing:(NSNotification *)note {
NSString *enteredName = [[[note userInfo] valueForKey:#"NSFieldEditor"] string];
if ([enteredName isEqualToString:defaultTagName]) {
NSString *dString = [NSString stringWithFormat:#"Rejected - Name cannot be default value of '%#'", defaultTagName];
NSString *errDescription = NSLocalizedStringFromTable( dString, #"Tag", #"validation: default name error");
NSString *errRecoverySuggestion = NSLocalizedStringFromTable(#"Make sure you enter a unique value for the new tag.", #"Tag", #"validation: default name error suggestion");
int errCode = TAG_NAME_DEFAULT_VALUE_ERROR_CODE;
NSArray *objArray = [NSArray arrayWithObjects:errDescription, errRecoverySuggestion, nil];
NSArray *keyArray = [NSArray arrayWithObjects:NSLocalizedDescriptionKey, NSLocalizedRecoverySuggestionErrorKey, nil];
NSDictionary *eDict = [NSDictionary dictionaryWithObjects:objArray forKeys:keyArray];
NSError *error = [[NSError alloc] initWithDomain:TAG_ERROR_DOMAIN code:errCode userInfo:eDict];
NSBeep();
[preferencesWindowsController presentError:error];
unsigned long index = [self rowWithDefaultTag];
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO];
// [self editColumn:0 row:index withEvent:nil select:YES];
}
}
- (unsigned long)rowWithDefaultTag {
__block unsigned long returnInt;
[managedTags enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj valueForKey:#"name"] isEqualToString:defaultTagName]) {
returnInt = idx;
*stop = YES;
}
}];
return returnInt;
}
With the 'editColumn' line commented out, the code works, so if the user accepts the default tag name without editing it, the error is built, displayed and the process finishes by leaving the appropriate row in the table highlighted.
However, I would like to take it that step further and place the user in edit mode. When I uncomment the 'editColumn' line, the behaviour is not at all what I expected - the tableView loses its blue focus box and the row that respresents the new tag is blank. If I click on the tableView, the row becomes visible. I've spent a lot of time on this and have got nowhere, so some help with this would be very much appreciated.
(Note: I tried using textDidEndEditing, which also didn't behave as I expected, but that is a separate issue!)
Answering my own question. Doh!
I already had a method which I used to put the user in edit mode when they clicked the button to add a new tag:
- (void)objectAdded:(NSNotification *)note {
if ([[note object] isEqual:self]) {
[self editColumn:0 row:[self rowWithDefaultTag] withEvent:nil select:YES];
}
}
Creating a notification to call this solves the problem and places the user in edit mode correctly. The important thing is not to try to do this on the existing runloop; so sending the notification as follows postpones delivery until a later runloop:
// OBJECTADDED is a previously defined constant.
NSNotification * note = [NSNotification notificationWithName:OBJECTADDED object:self];
[[NSNotificationQueue defaultQueue] enqueueNotification: note postingStyle: NSPostWhenIdle];
Problem solved. I wasted a lot of time trying to solve this - a classic example of getting too involved in the code and not looking at what I'm trying to do.
I've forgotten where I first saw this posted - whoever you are, thank you!