Handling the paste event in a UIViewController - iphone

I would like to handle the paste event in my UIViewController for my attached UITextView, so that if the pasted data is text or html it should paste the data in as normal and as it already does, but if it is something else like a image or pdf or anything, I would like to store the data locally in another way and not paste it into the UITextView (as it can not do this).
How would I go around doing this? In the only example I can find from Apple, they know the type of the data they paste. Even getting my "-(void)paste:(id)sender" method in my controller to get called when there is a paste event in my UITextView escapes me.
Thank you

You can use the method canReadObjectForClasses:options: to test for something without actually retrieving the value.
For example, testing for strings:
NSArray *classes = [NSArray arrayWithObject:[NSString class]];
NSDictionary *options = [NSDictionary dictionary];
if ([[NSPasteboard generalPasteboard] canReadObjectForClasses:classes options:options]) {
NSLog(#"yup, there is a string in here");
}
Update:
You're checking for all objects in the paste board this way, just add any class that you expect on the array.
NSArray *classes = [NSArray arrayWithObjects:[NSURL class], [NSString class], nil];
Use the UIResponderStandardEditActions protocol to implement the paste method.

Related

Accessing NSDictionary problem

I'm only new to iPhone development, so my apologies if this is a silly question. I'm building various apps based on a book I'm reading and one of their suggestion was to build a mini web browser. I thought this would be easy, but while most of it is, I'm seriously struggling with the NSDictionary.
I have a UISegmentedControl used to display various bookmarks. The bookmark name that is displayed on the buttons of the UISegmentedControl is going to be my key and the url is the value associated with it.
I first try to declare an NSDictonary as a private (global variable), but since I could not get it to work, I resorted to declare it in my header file as follows:
#property (nonatomic, retain) NSDictionary *bookmarks;
I synthesize it and I initialized it in the viewDidLoad as follows:
- (void)viewDidLoad
{
bookmarks = [NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft",
#"http://www.google.com",
#"Google",
#"http://www.apple.com",
#"Apple",
#"http://msdn.microsoft.com",
#"MSDN", nil];
[super viewDidLoad];
}
I then associated a control with my segmented control and when the event is triggered and the function is called I've got the following code which is called:
- (IBAction) getShortcut:(id)sender
{
NSString *shortcut;
shortcut = [shortcuts titleForSegmentAtIndex:shortcuts.selectedSegmentIndex];
NSString *url = [bookmarks valueForKey:shortcut];
//[self navigateTo: url];
[url release];
}
When a button from the UISegmentedControl is clicked, I extract the value and stored it into shortcut and then I try to use the shortcut variable as a key to extract the associated value from the NSDictionary "bookmarks" but it keeps crashing on NSString *url = [bookmarks valueForKey:shortcut];
and bombs out of my function and displays the usual error EXC_BAD_ACCESS
Any help would be greatly appreciated.
T.
You have two options. one is to deal with the ivar directly as #Matt S. posted. Note that in this case you need to keep you object with enough retain count. You're using and auto released object and causing the error.
The other option is to use the property you already defined:
self.bookmarks = [[NSDictionary ...]];
And the property retains it.
That dictionary is autoreleased.
Try this:
self.bookmarks = [NSDictionary dictionaryWithObjectsAndKeys...]
You didn't retain the NSDictionary.
Do:
bookmarks = [[NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft", nil] retain];
The problem is, that you do not retain "bookmarks" in the viewDidLoad method. There is an naming convention mentioned somewhere in the Apple docs: If an intialisation method starts with "init..." the returned object is retained, if not you have to do it yourself. The "dictionaryWithObjectsAndKeys" returns and object with retain count 0, which means, that after the scope of assignment (your viewDidLoad method) it is immediatly released again.
So just put a
[bookmarks retain];
after your initalisation and you are done. Another solution which does the retaining for you
bookmarks = [[NSDictionary alloc] initWithObjectsAndKeys ...];
And you shouldn't release the url in your action. It gets released, once you release the dictionary

creating a Mutable array that can be added to in later clicks of the same button?

General noob questions:
(1) How can I create an NSMutable array in a buttonClicked action that I can add more entries to during subsequent clicks of the same button? I always seem to start over with a new array at every click (the array prints with only 1 entry which is the most recent button's tag in an NSLog statement).
I have about 100 buttons (one for each character in my string called "list") generated by a for-loop earlier in my code, and each has been assigned a tag. They are in a scrollview within the view of my ViewController.
I wish to keep track of how many (and which ones) of the buttons have been clicked with the option of removing those entries if they are clicked a second time.
This is what I have so far:
-(void) buttonClicked:(UIButton *)sender
NSMutableArray * theseButtonsHaveBeenClicked = [[NSMutableArray alloc] initWithCapacity: list.length];
NSNumber *sendNum = [NSNumber numberWithInt:sender.tag];
[theseButtonsHaveBeenClicked addObject:sendNum at index:sender.tag];
NSLog(#"%#",theseButtonsHaveBeenClicked);
}
(2) I have read that I may be able to use a plist dictionary but I don't really understand how I would accomplish that in code since I cant type out the items in the dictionary manually (since I don't know which buttons the user will click). Would this be easier if I somehow loaded and replaced the dictionary in a plist file? And how would I do that?
(3) I also have no idea how I should memory manage this since I need to keep updating the array. autorelease?
Thanks for any help you can provide!
Okay, firstly you are creating a locally scoped array that is being re-initialised on every call to buttonClicked:. The variable should be part of the class init cycle.
You will also be better off with an NSMutableDictionary instead of an NSMutableArray. With a dictionary we don't have to specify capacity and we can use the button's tags as dictionary keys.
Here's what you need to do, these three steps always go together: property/synthesize/release. A good one to remember.
//Add property declaration to .h file
#property (nonatomic, retain) NSMutableDictionary * theseButtonsHaveBeenClicked;
//Add the synthesize directive to the top of .m file
#synthesize theseButtonsHaveBeenClicked;
// Add release call to the dealloc method at the bottom of .m file
- (void) dealloc {
self.theseButtonsHaveBeenClicked = nil; // syntactically equiv to [theseButtonsHaveBeenClicked release] but also nulls the pointer
[super dealloc];
}
Next we create a storage object when the class instance is initialised. Add this to your class's init or viewDidLoad method.
self.theseButtonsHaveBeenClicked = [[NSMutableDictionary alloc] dictionary]; // convenience method for creating a dictionary
And your updated buttonClicked: method should look more like this.
-(void) buttonClicked:(UIButton *)sender {
NSNumber *senderTagAsNum = [NSNumber numberWithInt:sender.tag];
NSString *senderTagAsString = [[NSString alloc] initWithFormat:#"%#",senderTagAsNum];
// this block adds to dict on first click, removes if already in dict
if(![self.theseButtonsHaveBeenClicked objectForKey:senderTagAsString]) {
[self.theseButtonsHaveBeenClicked setValue:senderTagAsNum forKey:senderTagAsString];
} else {
[self.theseButtonsHaveBeenClicked removeObjectForKey:senderTagAsString]; }
[senderTagAsString release];
NSLog(#"%#", self.theseButtonsHaveBeenClicked);
}

trying to add dictionary objects to an NSMutableArray - afterward array returns null?

I just did a search for my particular question and although the answers are close, I can't get my head around the answer...so I need some assistance.
I'd like to populate an array (NSMutableArray I suppose) with a group of dictionary objects that are parsed from JSON strings...the dictionary part I got, the JSON parsing I got, but when I try to put these objects into the NSMutableArray and NSLog it I get (null)... here are my steps (in a general way)
edit 1:
-The array I am creating is called NewFinalArray. it is an NSMutableArray, declared at the .h file and synthesized (and now alloc'd and init'd) as noted in the viewDidLoad method of the DetailViewController. It's contents are to be displayed in a UITableView.
-In DetailViewController, I have been successful in creating a plain NSArray/NSMutableArray and populating it with values that display in my UITableView.
-In the new scenario, I am receiving the information to be displayed through JSON strings which are retrievable through dictionary objects. I am using the Stig JSON libraries for iPHone/iPad. I have no problems there.
-All I wanted to do is getting the existing dictionary objects (which I can loop through from the existing array and see) and add them to a new Array to be used for displaying menu items in my UITableview.
I declared my mutableArray in my .h file
#interface blah : ...,...,...{
NSMutableArray *newFinalArray;
// other vars and IBOutlets
}
#property (nonatomic, retain) NSMutableArray *newFinalArray;
// other #property and (IBAction) stuff
#end
I then synthesize it in my .m file... I even alloc/inited it at viewDidLoad (it's a DetailViewController)
#synthesize this,that, newFinalArray; // keep track of newFinalArray, that's the one I want
- (void)viewDidLoad {
// other code
[[newFinalArray alloc] init]; // ya returns a warning, about not responding to alloc, but whatever (for now)
// I also tested of course without having to do that.
in my method that uses newFinalArray, the method is a recursive function that calls itself. each time it calls, it should add the dictionary object to the array (or does it?)
-(void)digTree:(NSArray *)array{
for (NSDictionary *dictionary in array){
// looping through the array
[self newFinalArray addObject:[dictionary]];
// more other code, and somewhere along the way I recurse
[self digTree:anotherArray];
}
}
when I try to NSLog (#"my final array is %#", newFinalArray) I get (null).
I am probably missing something here. I tried to add "nil" at the end. I am a little new/green to this , so if someone can lend a hand and let me know how to populate my newFinalArray with these dictionary objects it would be most appreciated.
[[newFinalArray alloc] init];
should be:
newFinalArray = [[NSMutableArray alloc] init];
This line is wrong too:
[self newFinalArray addObject:[dictionary]];
it should be:
[newFinalArray addObject:dictionary];
The first thing I notice that is wrong, is it should be:
newFinalArray = [[NSMutableArray alloc] init];
in viewDidLoad. See if that fixes it. It looks like there are other things wrong as well, so turn on warnings and see what else the compiler warns you about for hints.
How are the dictionaries stored? An alternative/probably easier way to do this would probably be to use arrayWithObjects:. Also, when using addObject:, there is no need to add nil (in fact, you can't add nil).

Custom Classes not being retained

I have a NSXMLParser that parses YT's API. It then turns all the videos into a class I wrote called video. Then the class is put into an array. Then back at my rootviewcontroller I access xmlParser.allVideos (xmlParser is a class I wrote that is the delegate for the xml parser.
Here is what I do with it in the viewDidLoad:
arrayFromXML = xmlParser.allVideos;
Then in the drawing of the tableView I do this:
tempVideo = [arrayFromXML objectAtIndex:indexPath.row];
cell.textLabel.text = tempVideo.videoTitle; //crashes here and says NSString has been deallocated.
How can I fix this?
If arrayFromXML is an instance variable you have to retain or copy (to be safe from later manipulation) the array as xmlParser simply might not be alive anymore when other methods are called later:
arrayFromXML = [xmlParser.allVideos copy];
Or better yet using a copy or retain property:
self.arrayFromXML = xmlParser.allVideos;

Three20: Make a TTTableItem link to a TTViewController

I want my table items to show the disclosure arrow and link to another "details" screen. Pretty standard. Three20 is supposed to help with this.
In my TTListDataSource, I'm currently doing:
- (void)tableViewDidLoadModel:(UITableView *)tableView
{
[super tableViewDidLoadModel:tableView];
// Construct an object that is suitable for the table view system
// from each domain object that we retrieve from the TTModel.
for (Listing *result in [(id<SearchResultsModel>)self.model results]) {
NSString *url = [NSString stringWithFormat:#"myapp://listing", result.theID];
TTTableSubtitleItem *item = [TTTableSubtitleItem itemWithText:result.title subtitle:result.desc imageURL:result.imageURL defaultImage:nil URL:url accessoryURL:nil];
[self.items addObject: item];
}
}
But I need to pass an object (not just a string) to the TTViewController when the TTTableItem is clicked. I know that you can pass an NSDictionary object to a
TTViewController using: TTNavigator's [[TTNavigator navigator] openURL: query: animated:] method.
However, since I'm trying to open this URL from a TTTableItem, I don't know how to use openURL method with it where I can pass a query NSDictionary object, since all it takes as an NSString "URL" parameter.
I read the discussion here however I'd really, really like to avoid having to modify the original Three20 code. If it can properly be extended, that would be fine.
What's the simplest way I can make my table item's open up a TTViewController and pass an object (such as an NSDictionary)?
If you look at http://three20.info/core/navigation they show you how to do this without overriding the didSelectObject method.
In a nutshell, you can assign a URL to an object (via a category for NSObject) which is mapped to a URL for the controller that is hosting the table. The handler can then open another URL passing the appropriate objects in the query dictionary.
From the Google Group...
http://groups.google.com/group/three20/msg/5cdbbaf753dc52a1