getting string from one ViewController to another? - objective-c

I have been working on my application for a while now. I am stuck on this small problem: setting my text field's text isn't working.The string of the text field is called from another viewcontroller Here is what my code looks like:
1st view controller
-(void)setURL:(NSURL *)src
{
downloadSourceField.text = src;
}
2nd view controller
if ([[booksArray objectAtIndex:indexPath.row] isEqual:[NSString stringWithFormat:#"%#",song_1]])
{
NSString *sc1 = [NSString stringWithFormat:#"tst 1"];
[tc setURL:sc1];
}
else
{
NSString *sc = [NSString stringWithFormat:#"test 2"];
[tc setURL:sc];
}

I think you're trying to assign an NSURL object to an NSString variable. What you should do is
downloadSourceField.text = [src absoluteString];
The absoluteString method will convert your NSURL into an NSString representation.

Related

How to add multiple email attachments using UIActivityItemProvider

I'm working on an iOS app in which I want to add MULTIPLE attachments to an email using UIActivityItemProvider. I want to do it using UIActivityItemProvider because I do not want to incur the overhead of processing the record before I display the UIActivtyViewController to the user. If I pass in one image using an NSData or an NSURL object to the "url" element of the returned object, then the inline image shows fine. If I pass in an array of these objects then nothing shows up. I believe that passing an array will work if I use the ActivityItems parameter when initializing an NSActivityViewController, but again, I do not want to do this because I want to take advantage of the delayed processing available by using the UIActivityItemProvider. Below is my code
#implementation NoteRecordActivityProvider
- (id)initWithPlaceholderItem:(id)placeholderItem
{
//Initializes and returns a provider object with the specified placeholder data
return [super initWithPlaceholderItem:placeholderItem];
}
- (id)item
{
// //Generates and returns the actual data object
NSData *imageFile = [[NSData alloc]init];
NSString *imageFileName;
NSURL *url;
NSString* exportPath;
NSMutableArray* imageArray = [[NSMutableArray alloc]initWithCapacity:0];
NSInteger photoCount = self.noteRecord.photoCount;
for (NSInteger i = 0; i < photoCount; i+=1)
{
//Add File Attachment
PhotoObject *po = (PhotoObject*)[self.noteRecord photoObjects:i];
NSString *photoGUID = [(PhotoObject*)[self.noteRecord photoObjects:i]GUID];
imageFile = ImageDataReturningMethodHere;
imageFileName = [[NSArray arrayWithObjects:#"Image", [NSString stringWithFormat:#"%ld", (long)i], #".png", nil] componentsJoinedByString:#""];
exportPath = [[FileSystemProvider exportPath] stringByAppendingPathComponent:imageFileName];
[imageFile writeToFile:exportPath atomically:YES];
url = [NSURL fileURLWithPath:exportPath];
[imageArray addObject:url];
}
if ([self.activityType isEqualToString:UIActivityTypeMail])
return imageArray;
else
return nil;
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return #{#"body":#"", #"url":[[NSURL alloc]init]};
}
-(NSString *) activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType {
return [NSString stringWithFormat:#"Attached Record: %#", self.noteRecord.title];
}
#end
I did find the answer to this question. First I created an PhotoAttachmentActivityProvider that had a property for the source document which contains the photo I wanted to attach, and an index to the attachment in that document. I'm pasting my code here which uses a custom document called a NoteRecord:
#interface EMailPhotoAttachmentItemProvider : UIActivityItemProvider
#property (nonatomic, readwrite) NSInteger photoIndex;
#property (nonatomic, strong) NoteRecord* noteRecord;
#end
Then when I am showing the UIActivityViewController I add 1 of these custom UIActivityItemProvider objects for each attachment:
for (NSInteger i = 0; i < self.noteRecord.photoCount; i++)
{
EMailPhotoAttachmentItemProvider* photoProvider = [[EMailPhotoAttachmentItemProvider alloc]initWithPlaceholderItem:#{#"body":textToShare, #"url":url}];
photoProvider.photoIndex = i;
photoProvider.noteRecord = self.noteRecord;
[activityProviders addObject:photoProvider];
}
//Initialize the ActivityViewController
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityProviders applicationActivities:applicationActivities];
Then in the custom UIActivityItemProvider I check for whether I'm processing a EMAIL, and then I create a URL for the image using the document and image index provided:
#import "EMailPhotoAttachmentItemProvider.h"
#import "MiscUtilities.h"
#import "FileSystemProvider.h"
#implementation EMailPhotoAttachmentItemProvider
- (id)initWithPlaceholderItem:(id)placeholderItem
{
//Initializes and returns a provider object with the specified placeholder data
return [super initWithPlaceholderItem:placeholderItem];
}
- (id)item
{
if ([self.activityType isEqualToString:UIActivityTypeMail])
{
// Code here gets the image file from the NoteRecord at the PhotoIndex provided to
// the UIActivityItemProvider at the imageIndex, creates a URL for that image and returns it here.
// Your implementation will vary
PhotoObject *po = (PhotoObject*)[self.noteRecord photoObjects:self.photoIndex];
NSString *photoGUID = [(PhotoObject*)[self.noteRecord photoObjects:self.photoIndex]GUID];
NSData *imageFile = [[[MiscUtilities getApplicationDelegate]imageProvider]imageDataWithCaptionFromGUID:photoGUID caption:po.caption maxResolution:600];
NSString *imageFileName = [[NSArray arrayWithObjects:#"Image", [NSString stringWithFormat:#"%ld", (long)self.photoIndex], #".png", nil] componentsJoinedByString:#""];
imageFileName = [[FileSystemProvider exportPath] stringByAppendingPathComponent:imageFileName];
[imageFile writeToFile:imageFileName atomically:YES];
NSURL *url = [NSURL fileURLWithPath:imageFileName];
return url;
}
else
{
return nil;
};
}
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
NSString* defaultImagePath = [[FileSystemProvider imagePath]stringByAppendingPathComponent:#"default.png"];
NSURL *url = [[NSURL alloc]initWithString:defaultImagePath];
return #{#"body":#"", #"url":url};
}
#end

Why is this AsyncImageView not loading the image?

I have the following code:
-(void) viewDidAppear:(BOOL)animated {
AsyncImageView *weatherImageView = [[AsyncImageView alloc] initWithFrame:weatherImage.frame];
NSString *myString = #"http://openweathermap.org/img/w/10d.png";
NSString *url = [NSString stringWithString:myString];
NSLog(#"The complete url is %#", url);
[weatherImageView loadImageFromURL:url];
[self.weatherImage addSubview:weatherImageView];
}
The image URL is all fine but the image does not show up when the view appears.
But if I write the following line in the same method
-(void) viewDidAppear: (BOOL) animated {
[self.weatherImage setImage:[UIImage imageNamed:[UIImage imageNamed:"background.png"]]];
}
I see that the image referred by "background.png" appears neat in the ImageView.
Wondering what is wrong with the first code.
loadImageFromURL expects a NSURL. Not a NSString:
AsyncImageView *weatherImageView = [[AsyncImageView alloc] initWithFrame:weatherImage.frame];
NSString *myString = #"http://openweathermap.org/img/w/10d.png";
NSURL *url = [NSURL URLWithString:myString];
NSLog(#"The complete url is %#", url);
[weatherImageView loadImageFromURL:url];

Why would a property essentially disappear? Obj-C, Cocoa

I am quite stumped. I have an app with a class for storing item details. Called LEItem. Those items are stored in a store with a class labeled LEItemStore. I have a view with a table of all items. This works fine. If you tap on a row, it sends this message to LogbookFirstViewController.
LogbookFirstViewController *logController = [[LogbookFirstViewController alloc] initForNewItem:NO];
NSArray *items = [[LEItemStore sharedStore] allItems];
LEItem *selectedItem = [items objectAtIndex:[indexPath row]];
NSString *description = [selectedItem description];
NSLog(#"%#", description);
[logController setItem:selectedItem];
[self dismissViewControllerAnimated:YES completion:nil];
That is in a TableView class. In the LogbookFirstViewController.m I have
-(void)setItem:(LEItem *)i{
item = i;
NSString *t = [item description];
NSLog(#"In LogbookFirstViewController, returning %#", t);
}
This is where it gets odd. That works. It outputs the correct item, therefore I would think everything would be okay. But it's not. item is a class-level property, so it should stay, but it doesn't. In the same class, I have overrode this method.
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//NSString *string = [item description];
//NSLog(#"Item = %#", string);
NSLog(#"View did Appear:animated");
int glucoseValue = [item glucose];
NSString *glucoseString = [NSString stringWithFormat:#"%d", glucoseValue];
[glucoseField setText:glucoseString];
int proteinValue = [item protein];
NSString *proteinString = [NSString stringWithFormat:#"%d", proteinValue];
[proteinField setText:proteinString];
int carbsValuue = [item carbs];
NSString *carbsString = [NSString stringWithFormat:#"%d", carbsValuue];
[carbsField setText:carbsString];
int insulinValue1 = [item insulin];
NSString *insulin1String = [NSString stringWithFormat:#"%d", insulinValue1];
[insulinField1 setText:insulin1String];
int insulinValue2 = [item insulin2];
NSString *insulinString2 = [NSString stringWithFormat:#"%d", insulinValue2];
[insulinField2 setText:insulinString2];
//NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
//[dateFormatter setDateStyle:NSDateFormatterShortStyle];
//[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
//NSLog(#" The item was created on %#", [dateFormatter stringFromDate:[item dateCreated]]);
//[dateButton setTitle:[dateFormatter stringFromDate:[item dateCreated]] forState:UIControlStateNormal];
NSString *t = [item description];
NSLog(#"Loading view... Returns: %#", t);
}
I know that it isn't the cleanest code, but the idea is the same. It uses exactly the same code as the setItem: method. However, this always returns (null). Why? The property appears to go missing at viewWillAppear.
Thanks.
EDIT
I solved the problem. As you can see, the checked answer below did give the right idea, here is what I did to solve it. The problem was that when I sent setItem: I used this code to get LogbookFirstViewController
LogbookFirstViewController *logController = [[LogbookFirstViewController alloc] initForNewItem:NO];
As I know see, that created a new instance of LogbookFirstViewController, so therefore, the existing one did not change it's Item property, as properties are assigned to one instance. Therefore, I was only changing the value of Item for this "invisible" property.
To solve this, one must get the existing instance of the viewController. To do this I did the following:
In LogbookFirstViewController.h I added this property
#property (assign) LogbookFirstViewController *instance;
Then, synthesize instance in your .m and in the same placed I added this to viewDidLoad
- (void)viewDidLoad
{
instance = self;
...
Then, in the other viewController, entriesViewController, I added this too the .h
#property (nonatomic, strong) LogbookFirstViewController *logController;
Synthesize it. Then, I just used my didSelectRowAtIndexPath the same way, just using the existing logController
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *items = [[LEItemStore sharedStore] allItems];
LEItem *selectedItem = [items objectAtIndex:[indexPath row]];
NSString *description = [selectedItem description];
NSLog(#"%#", description);
NSLog(#"Setting controller: %#", logController);
[logController setItem:selectedItem];
[self dismissViewControllerAnimated:YES completion:nil];
}
Then it works!
You have a line where you create the LogbookFirstViewController but you don't actually cause it to display anything (push or present). Since it's a local variable, it would appear that whatever instance of that controller is loading its view is not the same one that you initialize in the code you've shown.
You can verify this by adding a couple of NSLog lines, such as:
NSLog(#"Setting controller: %#", logController); // Insert before existing line
[logController setItem:selectedItem];
...and...
[super viewWillAppear:animated];
NSLog(#"Viewing controller: %#", self); // Insert after existing line
For things to work the way you want, those have to print the same address.
You should retain when assigning object to property without ARC:
-(void)setItem:(LEItem *)i{
_item = [i retain];
...
}
If you use property with ARC, then write _item = i;:
-(void)setItem:(LEItem *)i{
_item = i;
...
}

UITableView reloadData crashes with error [__NSCFConstantString objectForKey:]

I am parsing data from server and display this data in my app. This data is a JSON data and it looks like this:
{"getMessages":[{"msgid":"1","message":"Hello.","dateposted":"2012-08-28"}]}
That's when a message is available to be sent, however, if no messages were available, JSON will look like this:
{"status":"No messages available"}
In my app, I use NSJSONSerialization to parse the JSON. Here is how I do it:
if ([data length] > 0)
{
NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:data options
NSJSONReadingMutableContainers error:nil];
if (![parsedData objectForKey:#"getMessages"])
{
[self.messageArray addObject:#"No Messages"];
}
else
{
self.messageArray = (NSMutableArray *)[parsedData objectForKey:#"getMessages"];
}
}
As you can see, when the parsedData has no getMessages key, it will add the No Messages in
self.messageArray, but if it has the key, it will add the values related to it.
self.messageArray was the array I used to populate the messageTable. At the end of the download, I put the code [messageTable reloadData].
The problem is this: Reloading the table works if the parsedData contains the key getMessages. However, if the key was not found, reloading the table crashes.
This is my tableView:cellForRowAtIndexPath method:
NSString *tableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableIdentifier] autorelease];
if (tableView == messageTable) //I do this since there is another table I am using
{
NSString *string = [NSString stringWithFormat:#"%#", [self.messageArray objectAtIndex:0]];
if ([string isEqualToString:#"No Messages"])
{
cell.textLabel.text = string;
}
else
{
NSDictionary *dict = [self.messageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [dict objectForKey:#"message"]];
}
}
In messageTable, I check first if the first index of self.messageArray is equal to the string "No Messages", this is to let the user know that no messages can be retrieved. If the
string is not equal, it will then assume that the data inside the array is a dictionary and therefore, it will be parsed to display the message.
After making use of breakpoints and logs, I realized that the crash happens while reloading the table. I inserted a breakpoint and a log at the start of the method tableView:cellForRowAtIndexPath but it never even got there. I tried checking the content of the self.messageArray and it does contain "No Messages".
The crash tells me this error: [__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x10ed84
I know that this error is telling me that I am calling the method objectForKey in a NSString, but I really don't know why. Can anyone help me here?
try this
[self.messageArray removeAllObjects];
[self.messageArray addObject:[parsedData setObject:#"No Messages" forKey:#"getMessages"]];
mostly the dictionary is not setting for the key.. just check..
Also,
NSDictionary *diction = [self.messageArray objectAtIndex:0];
NSString *string = [NSString stringWithFormat:#"%#", [diction objectForKey:#"getMessages"]];
if ([string isEqualToString:#"No Messages"])
{
cell.textLabel.text = string;
}
else
{
NSDictionary *dict = [self.messageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [dict objectForKey:#"message"]];
}
First make sure that messageArray is defined as NSMutableArray... and if you are reloading your parsedData from time to time then try this
if (![parsedData objectForKey:#"getMessages"])
{
[self.messageArray removeAllObjects];
[self.messageArray addObject:#"No Messages"];
}
else
{
[self.messageArray removeAllObjects];
self.messageArray = [[parsedData objectForKey:#"getMessages"] mutableCopy];
}

Null output troubles

Okay I've been trying at this for about 2-3 hours now and I don't seem to quite get it. Here is the code and a brief explanation:
I'm trying to make two lists of words, pull one word from each of those lists at random, and display both words (along with a third) on the screen when a button is pressed. Here is the code:
#import "Project001ViewController.h"
#implementation Project001ViewController
-(ArrayOfWords *) advs
{
if(!advs){
advs = [[ArrayOfWords alloc] init];
NSString* advpath = #"/WordLists/adverbs.txt";
NSLog(#"1");
[[self advs] populateListOfWords:advpath];
}
return advs;
}
-(ArrayOfWords *) adjs
{
if (!adjs) {
adjs = [[ArrayOfWords alloc] init];
NSString* adjpath = #"/WordLists/adjectives.txt";
[[self adjs] populateListOfWords:adjpath];
NSLog(#"2");
}
return adjs;
}
- (IBAction)generate:(UIButton *)sender;
{
//int randy = arc4random() % 11;
//NSNumber* num= [NSNumber numberWithInteger:randy];
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
//NSLog(#"%#",thePhrase);
[display setText:#"Hi"];
}
#end
I'm having trouble on the last NSLog line:
NSString* obj = #"app";
NSString* adverb = [[self advs] randomItem];
NSString* adjective = [[self adjs] randomItem];
NSLog(#"%i %i",[adjs size],[advs size]);
NSLog(#"1 %# %# %#.",adverb, adjective, obj);
Instead of getting the two randomly selected words (using arc4random() to produce them) the array returns Null. But I know FOR CERTAIN. That the array's are not empty because the NSLog Line where I print [adjs size] and [advs size] I get the correct sizes of the list of words. I just want to know what is causing them to print Null here.
populateListOfWords, randomItem, and size methods:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
-(NSString*) randomItem{
//returns random object in list
return (NSString*)[list objectAtIndex:(arc4random() % (int)length)] ;
}
-(int) size{
//returns size of list
return (int)length;
}
(If more code is needed let me know and thank you in advanced for any and all help).
I believe there is a problem with the paths. It is impossible to have access to the path /WordLists/adjectives.txt in iOS due to the application sandbox. I suggest you add these files to the application by dragging and dropping them onto the project. You can get the file paths for resources in application bundle using
NSString * path = [[NSBundle mainBundle] pathForResource:#"adjectives" ofType:#"txt"];
Now pass this path to the method populateListOfWords:.
Because of the incorrect path, I believe wordListString is nil and everything else follows to be that.
Another thing is that int and NSNumber are not toll free bridged like NSStrings and other foundation objects. So
length=(NSNumber*)([words count]);
is incorrect. I suggest you define length as int or better NSUInteger to match the type count method returns.
This method is the problem:
- (NSArray *) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
length=(NSNumber*)([words count]);
return words;
}
It wasn't actually putting the words in a list that anyone else could access. I had to just modify it like so:
- (void) populateListOfWords:(NSString *) path {
//gets the components of the file into an NSString
NSString *wordListString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//returns an array of all the words (uses the next line indicator '\n' to know when it's at the end of the word
NSArray* words = [wordListString componentsSeparatedByString:#"\n"];
list = words;
length=(int)([words count]);
}
Now it gives me the correct output. But for some reason when I press the button twice it crashes. Oh well that's a new problem. Thanks again for all the help.
UPDATE
Turns out advs and adjs were being released so the second go around it was trying to access a nil value because when I call [self advs] [self adjs] the pointers exist, but their contents do not. I had to go back and refill them each time basically removing the if (!advs) and if (adjs) parts. It now works as intended.