I'm kind of a noob so hopefully this isn't too complex. I'm having a problem with my code that has so far stumped and confused me and some other users. I spent some time cleaning my code, and following some tutorials, but I still get past this issue. I just know it is something really simple, so here goes:
Im making an iOS app for my blog site. It goes to the site, pulls an XML document (containing the blog title, brief description, and full article url), and parses it to display in a UITableView. To manage this, there are two custom objects: "PostList", and "Post". Post is just an object that hold the individual post data (url, title, publication date, etc) and PostList has an array that holds the Post objects, as well as some getter/setter/init functions.
That all works well. The view loads and the XML is parsed and displays in the list. The problem arises when the user either taps on a link or scrolls some cells off the screen. As far as I can tell, these are both related to the same problem. In the case of the user tapping on the cell, the view controller does not pass a value to the detailed view controller (prepareForSegue), and in the case of scrolling, the cellForRowAtIndexPath does not reinitialize the cell values.
I believe that this is al due to the same problem, that once the view has been initialized, the existing values in the PostList array are somehow deleted. The debugger says the array still sees objects inside it, but they are all blank objects. I have spent forever trying to figure out why.
If you could give me some ideas why this is happening that would be AMAZING! My code is below. I posted almost all of it since Ive had issues with not posting enough before. Hope this helps. If you need more please ask!
-Thanks in advance ;)
FirstViewController.h
#import <UIKit/UIKit.h>
#import "PostList.h"
#import "Post.h"
#interface FirstViewController : UITableViewController
#property (nonatomic, strong) PostList *postController;
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#property (nonatomic, strong) Post *postToSendToDetailView;
#property (nonatomic, strong) NSString *type;
#end
FirstViewController.m
#import "FirstViewController.h"
#import "DetailViewController.h"
#import "Post.h"
#import "PostList.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.type = #"blog";
self.postController = [[PostList alloc] initWithType:self.type];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.postController countOfList];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Post *postAtSighting = [self.postController objectInListAtIndex:indexPath.row];
// Configure the cell...
[[cell textLabel] setText:postAtSighting.name];
[[cell detailTextLabel] setText:postAtSighting.content];
NSLog(#"Cell Initalized");
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"pushToDetailView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *destViewController = segue.destinationViewController;
destViewController.aPost = [self.postController objectInListAtIndex:indexPath.row];
}
}
#end
PostList.h
#import <Foundation/Foundation.h>
#import "Post.h"
#interface PostList : NSObject
-(NSMutableArray *) getPostsofType:(NSString *)type;
-(NSString *) getName: (Post *)aPost;
-(NSUInteger) countOfList;
-(Post *)objectInListAtIndex:(NSUInteger)theIndex;
-(id)initWithType:(NSString *)type;
#property (nonatomic, strong) NSMutableArray *postsArray;
#property (nonatomic) NSString *urlString;
#property (nonatomic) NSUInteger countingIndex;
#end
PostList.m
#import "PostList.h"
#import "TBXML+HTTP.h"
#implementation PostList
- (id)initWithType:(NSString *)type {
self = [super init];
if (self) {
//Creates postArray to store Post objects
self.postsArray = [[NSMutableArray alloc] init];
//Create other resources
self.urlString = [[NSString alloc] init];
self.countingIndex = 0;
[self getPostsofType:type];
}
return self;
}
-(NSString *)getName:(Post *)somePost {
//Gets the Name of the Post object and returns the value
NSString *name = somePost.name;
return name;
}
-(NSMutableArray *)getPostsofType:(NSString *)type {
//Go to the Server and get the posts that are available for the post type selected.
//Determing the post type
if ([type isEqual: #"blog"]) {
self.urlString = #"http://www.biteofanapple.com/blog/feeds/blogFeed.xml";
}
if ([type isEqual: #"bbp"]) {
self.urlString = #"http://www.biteofanapple.com/blog/feeds/blogFeed.xml";
}
//Checks for an existant xmlBlogData.xml
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get documents directory
NSString* blogXML = [documentsDirectory stringByAppendingPathComponent:#"xmlBlogData.xml"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:blogXML];
NSData *data = [[NSData alloc] init];
//Determines the best way to handle the xml file
if (fileExists) {
data = [NSData dataWithContentsOfFile:blogXML];
} else {
NSURL *url = [NSURL URLWithString:self.urlString];
//Goes to the Server and reads the XML file of the Posts
data = [NSData dataWithContentsOfURL:url];
if (data != nil) {
NSLog(#" NSData value is not nil");
}
//Saves the XML data to a .dat file in the document's directory
NSError *error;
BOOL succeed = [data writeToFile:[documentsDirectory stringByAppendingPathComponent:#"xmlBlogData.dat"] atomically:YES];
if (!succeed){
// Handle error here
NSLog(#"Error: File was unable to be written to the documents directory.");
[error setValue:#"-1" forKey: #"File unable to be written"];
}
}
//Parses the XML file
TBXML *tbxml = [TBXML newTBXMLWithXMLData:data error:nil];
//Prints the data Variable to NSLog
NSLog(#"%#", [NSString stringWithUTF8String:[data bytes]]);
//Make some placeholder variables
if (tbxml.rootXMLElement == nil) {
NSLog(#"XML Document does not have rootElement. Error -1");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Sorry, there was an error" message:#"Could not find root XML element. Please contact support for help with this issue." delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil, nil];
[alert show];
}
TBXMLElement *element = tbxml.rootXMLElement;
TBXMLElement *nextElement = element->nextSibling;
NSLog(#"URLs have been set, preparing for parse/input. [PostList.getPosts]");
//Extracts the content of the XML file and saves it to values in the Post Class
do {
/**********
* This loop goes through the XML file looking for <item> tags that hold information
* about the blog posts. It finds <item> tags and scours them for <title>, <description>,
* <pubdate>, and <link> tags to put into the class variables for the Post Class (aPost).
**********/
NSString *stringElement = [TBXML elementName:element];
NSLog(#"%#", stringElement);
//Creates Post variable to put stuff in.
Post *aPost = [[Post alloc] init];
//Sorts through the header junk to find the first <item> tag.
if (![stringElement isEqualToString:#"item"]) {
if (!(element->firstChild)) {
if (!(element->nextSibling)) {
element = nil;
}
element = element->nextSibling;
}
element = element->firstChild;
}
//Once the first <item> tag is found, this code executes.
else {
//Now we move to the first child tag and scour its contents and its siblings
nextElement = [TBXML nextSiblingNamed:#"item" searchFromElement:element];
element = element->firstChild;
do {
//Here it loops over and over until all the parts have been collected.
stringElement = [TBXML elementName:element];
if ([stringElement isEqualToString:#"title"]) {
aPost.name = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:#"description"]) {
aPost.content = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:#"link"]) {
aPost.postURL = [TBXML textForElement:element];
}
if ([stringElement isEqualToString:#"pubdate"]) {
aPost.publicationDate = [TBXML textForElement:element];
}
element = element->nextSibling;
} while (element->nextSibling);
NSLog(#"%#", [self getName:aPost]);
NSLog(#"name %# content %#", aPost.name, aPost.content);
[self.postsArray insertObject:aPost atIndex:self.countingIndex];
self.countingIndex++;
element = nextElement;
}
} while ((element != nil));
return self.postsArray;
}
-(Post *)objectInListAtIndex:(NSUInteger)theIndex {
return [self.postsArray objectAtIndex:theIndex];
}
-(NSUInteger)countOfList {
return [self.postsArray count];
}
#end
Post.h
#import <Foundation/Foundation.h>
#class Post;
#interface Post : NSObject
#property (nonatomic, weak) NSString *name;
#property (nonatomic, weak) NSString *author;
#property (nonatomic, weak) NSString *publicationDate;
#property (nonatomic, weak) NSString *content;
#property (nonatomic, weak) NSString *postURL;
#end
Post.m
#import "Post.h"
#implementation Post
- (id)init {
self = [super init];
if (self) {
self.name = #"";
self.author = #"";
self.content = #"";
self.publicationDate = #"";
self.postURL = #"";
}
return self;
}
#end
As I posted in the comment above, the problem is you are using weak references in your Post class. Try this code instead:
#import <Foundation/Foundation.h>
#class Post;
#interface Post : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *author;
#property (nonatomic, strong) NSString *publicationDate;
#property (nonatomic, strong) NSString *content;
#property (nonatomic, strong) NSString *postURL;
#end
If you want to learn about the differences between strong and weak references, take a look at this question:
Differences between strong and weak in Objective-C
Also note that the default reference type for objective-c properties is strong, so you can omit the strong keyword if you wish.
Related
I have a UITableViewController which should display all serviceName and Car Model. I have used subtitle as my UITableViewCell style. serviceName and Car Model are present in two different tables in Parse. I wrote the query and I am able to fetch the objectIDs of serviceName and Car model from the table. however when I try and use the objectIds to fetch the data from the respective tables I get a null value as return.
This is my currentjobs.h file
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface currentJobs : UITableViewController{
NSMutableArray *currentjobs;
}
#property (strong, nonatomic) IBOutlet UITableView *currentjobTable;
#property (strong, atomic) NSString *servicerequestid;
#property (strong, atomic) NSString *serviceid;
#property (strong, atomic) NSString *carid;
#end
and this is my currentjobs.m file
#import "currentJobs.h"
#import <Parse/Parse.h>
#interface currentJobs ()
#end
#implementation currentJobs
#synthesize currentjobTable;
#synthesize servicerequestid;
#synthesize serviceid;
#synthesize carid;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *mechanicid = [PFUser currentUser].objectId;
PFQuery *query1 = [PFQuery queryWithClassName:#"ServiceStatus"];
[query1 whereKey:#"mechanic" equalTo:mechanicid];
[query1 findObjectsInBackgroundWithBlock:^(NSArray *mechanicobjects, NSError *error) {
if (!error) {
currentjobs = [[NSMutableArray alloc] initWithArray:mechanicobjects];
NSLog(#"%#", currentjobs);
}
[currentjobTable reloadData];
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return currentjobs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"currentjob" forIndexPath:indexPath];
// Configure the cell...
PFObject *mechanic = [currentjobs objectAtIndex:indexPath.row];
servicerequestid = [mechanic objectForKey:#"servicerequest"];
NSLog(#"%#", servicerequestid);
//query to find carid and serviceid
PFQuery *query2 = [PFQuery queryWithClassName:#"ServiceRequests"];
[query2 getObjectInBackgroundWithId:servicerequestid block:^(PFObject *servicerequestobject, NSError *error) {
if (!error){
serviceid = [servicerequestobject objectForKey:#"serviceName"];
carid = [servicerequestobject objectForKey:#"car"];
NSLog(#"%#", serviceid);
}
}];
//query to find servicename and display
PFQuery *query3 = [PFQuery queryWithClassName:#"services"];
[query3 getObjectInBackgroundWithId:serviceid block:^(PFObject *serviceNameobject, NSError *error) {
if (!error){
NSLog(#"objects Found");
cell.textLabel.text = [serviceNameobject objectForKey:#"serviceName"];
}
else if (error){
NSLog(#"Error Found");
NSLog(#"%#", error);
}
}];
//query to find car model and display
PFQuery *query4 = [PFQuery queryWithClassName:#"customerCars"];
[query4 getObjectInBackgroundWithId:carid block:^(PFObject *customercarobject, NSError *error) {
if (!error){
cell.detailTextLabel.text = [customercarobject objectForKey:#"model"];
}
}];
return cell;
}
#end
The query3 and query4 does seem to give me any kind of output. Where am I going wrong?
I don't think the problem has to do with setting the delegate and datasource to self, as when you create a UITableViewController these values are set by default. But all you need to do to test that is throw break points in those methods when the table view loads. This could be an asynchronous issue, meaning you don't actually have the data at the time you're trying to use it. I recommended cleaning up your cellForRowAtIndexPath method. I like to keep that method clean as what I'd do is actually use a DAO to hold my parse methods, or just have all those parse related calls in a method that can be called in the cellForRowAtIndexPath... Just looks better. Aside from that, I'm pretty sure this is an asynchronous issue, which is common when working with the numerous built in parse methods.
I don't see you are implementing UITableViewDataSource or UITableViewDelegate protocols. I don't see either that you set your CurrentJobs class as the delegate and datasource of your tableview. Without doing that, non of your UITableViewDelegate and UITableViewDatasource methods will be called in your CurrentJobs class.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface Image : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) UIImage *image;
#property (nonatomic, strong) NSString *imageId;
#end
#import "Image.h"
#import <Parse/Parse.h>
#interface TableViewController()
#property (nonatomic, strong) NSMutableArray *array;
#property (nonatomic, strong) NSString *imageId;
#end
#implementation TableViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self fetchAllImages];
}
#pragma mark Retrieve All Images from Parse Server
-(void)fetchAllImages {
PFQuery *query = [PFQuery queryWithClassName:#"imagePost"];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
for(PFObject *tempParseObject in objects){
NSString *imageName = tempParseObject[#"name"];
PFFile *imageFile = tempParseObject[#"image"];
NSString *imageId = tempParseObject.objectId ;
UIImage *image = [UIImage imageWithData:imageFile.getData];
Image *retrievedImage = [[Image alloc] init];
retrievedImage.image = image;
retrievedImage.name = imageName;
retrievedImage.imageId = imageId;
[self.array addObject:retrievedImage];
}
}];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.array.count;
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
if(!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
Image *retrievedImage = [self.array objectAtIndex:indexPath.row];
cell.imageView.image = retrievedImage.image;
cell.textLabel.text = retrievedImage.name;
self.imageID = retrievedImage.imageId;
return cell;
}
#end
So as a quick side note.. this is not a good way to retrieve an Image/PFFile from parse as it will cause a long running operation in the main thread but its purpose here will serve as a simple example for how you should structure your code here. I query classname and retrieve the image, name and objectId, which happens when the view loads. I store them in temporary variables. Then i initialize my custom Image class, and assign its properties to the temporary variables. I then add that Image object to my array. This array is then used for the table view delegate and datasource methods. finally i create another Image variable and assign it to my array at indexpath.row and set the TEXT and IMAGE values of the UITableViewCell there.
The imageId is used if needed. I don't support naming conventions like naming an NSMutableArray as 'array', however, it should be a property over a plain ivar, though it technically won't make a difference in this case. Hope this simple example helps
I'm having issue trying to load a thumbnail from JSON into a UITableView cell. The cell's UI is a xib file. I'm not able to parse the thumbnail part of the JSON and display it in the table view. It's been a while since I did this (I tend to work on more games than typical iOS apps), and the last time I did it was a slightly different implementation.
JSON:
{
"data" :
[
{
"user_id" : "3",
"username" : "Alex Perez",
"avatar_url" : "http://mywebsite.com/images/alex_avatar.png",
"message" : "Hello there?"
},
{
"user_id" : "4",
"username" : "Jon Doe",
"avatar_url" : "http://mywebsite.com/images/john_avatar.png",
"message" : "I'm here now"
},
{
"user_id" : "2",
"username" : "Sam Givens",
"avatar_url" : "http://mywebsite.com/images/sam_avatar.png",
"message" : "Can we have a meeting around 2?"
},
....
And the data in the Data.h:
#import <Foundation/Foundation.h>
#interface Data : NSObject
#property (nonatomic, readwrite) int user_id;
#property (nonatomic, strong) NSString *username;
#property (nonatomic, strong) NSString *avatar_url;
#property (nonatomic, strong) NSString *message;
- (void)loadWithDictionary:(NSDictionary *)dict;
#end
Data.m:
#import "Data.h"
#implementation Data
- (void)loadWithDictionary:(NSDictionary *)dict {
self.user_id = [[dict objectForKey:#"user_id"] intValue];
self.username = [dict objectForKey:#"username"];
self.avatar_url = [dict objectForKey:#"avatar_url"];
self.message = [dict objectForKey:#"message"];
}
#end
Here is I'm running into issues when declaring the property and assigning the property to the data, so then it can then be passed to the View Controller that is going to display it. I'm getting the compiler warning Incompatible pointer types assigning to UIImage * from NSString*. I don't understand why Xcode isn't complaining about the other properties like the UILabel and the UITextView.
Cell.m
#import "Cell.h"
#interface Cell ()
#property (nonatomic) int userID;
// UIImageView property created from Interface Builder
#property (strong, nonatomic) IBOutlet UIImageView *avatarImage;
#property (nonatomic, strong) IBOutlet UILabel *usernameLabel;
#property (nonatomic, strong) IBOutlet UITextView *messageTextView;
#end
#implementation Cell
- (void)loadWithData:(Data *)data {
self.userID = data.user_id;
// Here I get "Incompatible pointer types assigning to UIImage * from NSString*
self.avatarImage.image = data.avatar_url;
self.usernameLabel.text = data.username;
self.messageTextView.text = data.message;
}
#end
Finally the MessagesViewController where it is displayed:
#interface MessagesViewController ()
#property (nonatomic, strong) IBOutlet UITableView *tableView;
#property (nonatomic, strong) NSMutableArray *loadedChatData;
#end
#implementation MessagesViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.loadedChatData = [[NSMutableArray alloc] init];
[self loadJSONData];
}
- (void)loadJSONData {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"json"];
NSError *error = nil;
NSData *rawData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
id JSONData = [NSJSONSerialization JSONObjectWithData:rawData options:NSJSONReadingAllowFragments error:&error];
[self.loadedChatData removeAllObjects];
if ([JSONData isKindOfClass:[NSDictionary class]]) {
NSDictionary *jsonDict = (NSDictionary *)JSONData;
NSArray *loadedArray = [jsonDict objectForKey:#"data"];
if ([loadedArray isKindOfClass:[NSArray class]])
{
for (NSDictionary *chatDict in loadedArray)
{
Data *chatData = [[Data alloc] init];
[chatData loadWithDictionary:chatDict];
[self.loadedChatData addObject:chatData];
}
}
}
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"Cell";
Cell *cell = nil;
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:self options:nil];
cell = (Cell *)[nib objectAtIndex:0];
}
Data *data = [self.loadedChatData objectAtIndex:[indexPath row]];
[cell loadWithData:data];
return cell;
}
U have to modify the error line and use it as :
self.avatarImage.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:data.avatar_url]]];
Create a custom class for the nib and then place the certain code in it to load the image from direct URL ... this works
var stringUrl = "http://mywebsite.com/images/alex_avatar.png"
var url: NSURL = NSURL(string: stringUrl)!;
self.imageView1.sd_setImageWithURL(url, placeholderImage: UIImage(named: "no_image_small.png"));
Try the Below code, it may help you.
NSURL *url = [NSURL URLWithString:#"http://mywebsite.com/images/alex_avatar.png"];
UIImage *thumbnail = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];
if (thumbnail == nil) {
thumbnail = [UIImage imageNamed:#"noimage.jpg"] ; //if no image get from url mean show a dummy one
}
CGSize itemSize = CGSizeMake(80, 80); //your own size can replace with 80
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[thumbnail drawInRect:imageRect];
cell.thumbnailImageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I've read other answers about this and at first I had a (null) error, but that is now fixed. Now, whenever I log my NSMutableArray, which I created in "SelectTeacherTableViewController.h" and passed to the SendMessageViewController class, it creates an array, but it is empty.
Here is the code from my SelectTeacherTableViewController.h
#import <UIKit/UIKit.h>
#interface SelectTeacherTableViewController :
UITableViewController
{
NSMutableArray *recipients;
}
#property (strong, nonatomic) NSArray *teachers;
#property (strong, nonatomic) NSMutableArray *recipients;
#end
Here is the code from my SelectTeacherTableViewController, where I synthesize the recipients and add objects to them through a method. (Code shortened)
#import "ParseStarterProjectAppDelegate.h"
#import "SelectTeacherTableViewController.h"
#import <Parse/Parse.h>
#interface SelectTeacherTableViewController ()
#end
#implementation SelectTeacherTableViewController
#synthesize recipients;
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = #"Message";
self.recipients = [[NSMutableArray alloc] init];
[PFUser logInWithUsername:#"awesome" password:#"password"];
NSString *getCurrentUserSchoolKey = [[PFUser currentUser] objectForKey:#"schoolKey"];
NSString *currentUserSchoolKey = [NSString stringWithFormat:#"%#", getCurrentUserSchoolKey];
PFQuery *queryForTeachers = [PFUser query];
[queryForTeachers orderByAscending: #"username"];
[queryForTeachers whereKey: #"role" equalTo:#"Teacher"];
[queryForTeachers whereKey:#"schoolKey" equalTo:currentUserSchoolKey];
[queryForTeachers findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"%# %#", error, [error userInfo]);
}
else {
self.teachers = objects;
NSLog(currentUserSchoolKey);
[self.tableView reloadData];
}
}];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.recipients = [[NSMutableArray alloc] init];
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
PFUser *user = [self.teachers objectAtIndex:indexPath.row];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[recipients addObject:user.objectId];
}
else {
cell.accessoryType = UITableViewCellAccessoryNone;
[recipients removeObject:user.objectId];
}
NSLog(#"%#", recipients);
}
When I log the recipients here, they do contain all of the correct values. However, this is my SendMessageViewController where I pass the array from the SelectTeacherTableViewController:
SendMessageViewController.m (Code Shortened):
#import "SendMessageViewController.h"
#import <Parse/Parse.h>
#import <MobileCoreServices/UTCoreTypes.h>
#import "SelectTeacherTableViewController.h"
#interface SendMessageViewController ()
#end
#implementation SendMessageViewController
- (void)viewDidLoad
{
[super viewDidLoad];
SelectTeacherTableViewController *recipientList = [[SelectTeacherTableViewController alloc] init];
NSMutableArray *recipients = [[NSMutableArray alloc] initWithArray:recipientList.recipients];
NSLog(#"%#", recipientList);
}
It's here where when I log the recipients, the array is empty.
I've read about it a little and encountered some solutions which said some things about creating simpleton files, or using the AppDelegate to store global variables in, but none of those solutions seemed to work for me. I have a feeling that the data is being lost when I segue. Is there anywhere where I have gone wrong in my code? I'm a little new to Objective-C and this problem has been bugging me for hours. Thanks.
If you need to see any more code, just ask me. Thanks.
This: SelectTeacherTableViewController *recipientList = [[SelectTeacherTableViewController alloc] init]; creates a new object. Anything you added to a recipients array inside some other SelectTeacherTableViewController is irrelevant.
If you need to use the information that you've added to the original object, you must pass a reference to that object…not create a new one.
[[SelectTeacherTableViewController alloc] init] does NOT give you a reference to your existing recipient list, it creates a NEW ONE. In object oriented programming you can have multiple instances of the same class that are NOT the same object.
Class != Object
I'm struggling to populate a Table View using JSON Data from Youtube (V 2.1) which has been parsed(Logged the output in the console)
Every time I am loading the Table View Controller, nothing is populated. I have even created a 'Video' class (NSObject). I'm struggling to understand what I'm doing wrong.
The following is my code:
Video.h
#import <Foundation/Foundation.h>
#interface Video : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *description;
#property (nonatomic, strong) NSString *thumbnail;
#property (nonatomic, strong) NSString *uploadedDate;
#property (nonatomic, strong) NSURL *url;
// Designated Initializer
- (id) initWithTitle:(NSString *)title;
+ (id) videoWithTitle:(NSString *)title;
- (NSURL *) thumbnailURL;
- (NSString *) formattedDate;
#end
Video.m
import "Video.h"
#implementation Video
- (id) initWithTitle:(NSString *)title {
self = [super init];
if ( self ){
self.title = title;
self.thumbnail = nil;
}
return self;
}
+ (id) videoWithTitle:(NSString *)title {
return [[self alloc] initWithTitle:title];
}
- (NSURL *) thumbnailURL {
// NSLog(#"%#",[self.thumbnail class]);
return [NSURL URLWithString:self.thumbnail];
}
- (NSString *) formattedDate {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *tempDate = [dateFormatter dateFromString:self.uploadedDate];
[dateFormatter setDateFormat:#"EE MMM,dd"];
return [dateFormatter stringFromDate:tempDate];
}
#end
Table View Controller implementation file (the one I'm trying to populate)
#import "FilmyViewController.h"
#import "Video.h"
#interface FilmyViewController ()
#end
#implementation FilmyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *videoURL = [NSURL URLWithString:#"http://gdata.youtube.com/feeds/api/users/OrtoForum/uploads?v=2&alt=jsonc"];
NSData *jsonData = [NSData dataWithContentsOfURL:videoURL];
NSError *error = nil;
NSDictionary *dataDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
NSLog(#"%#",dataDictionary);
self.videoArray = [NSMutableArray array];
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
for (NSDictionary *vDictionary in videosArray) {
Video *video = [Video videoWithTitle:[vDictionary objectForKey:#"title"]];
video.title = [vDictionary objectForKey:#"title"];
video.description = [vDictionary objectForKey:#"author"];
video.uploadedDate = [vDictionary objectForKey:#"uploaded"];
video.url = [NSURL URLWithString:[vDictionary objectForKey:#"url"]];
[self.videoArray addObject:video];
}
}
- (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 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.videoArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Video *video = [self.videoArray objectAtIndex:indexPath.row];
// Configure the cell...
cell.textLabel.text = video.title;
cell.textLabel.text = video.description;
return cell;
}
/*
#pragma mark - Navigation
// In a story board-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
Here's the JSON which I'm trying to extract from.
I looked for similar topics but didn't get any appropriate solution for this.
Research Link-one and Link-two is what i have been trying to follow.
Please let me know if there is any better approach for this.
What am i missing here?
Solution
Changed
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
to:
NSArray *videosArray = dataDictionary[#"data"][#"items"];
Change
NSArray *videosArray = [dataDictionary objectForKey:#"items"];
to
NSArray *videosArray = dataDictionary[#"data"][#"items"];
Your items array is in the second level: rootJSON -> data -> items
This is my NSObject code;
Task.h
#import <Foundation/Foundation.h>
#interface Task : NSObject
#property (nonatomic,strong) NSString *name;
#property (nonatomic,assign) BOOL done;
-(id)initWithName:(NSString *)name done:(BOOL)done;
#end
Task.m
#import "Task.h"
#implementation Task
#synthesize name = _name;
#synthesize done = _done;
-(id)initWithName:(NSString *)name done:(BOOL)done {
self = [super init];
if (self) {
self.name = name;
self.done = done;
}
return self;
}
This is my send mail code
Task *task = [[Task alloc]init];
MFMailComposeViewController *sendmail = [[MFMailComposeViewController alloc]init];
[sendmail setMailComposeDelegate:self];
NSString *message = [_tasks addObject:task]; // Error is here.
[sendmail setMessageBody:message isHTML:NO];
[sendmail setSubject:#"Test"];
[self presentViewController:sendmail animated:YES completion:nil];
I don't know, How to do it. I just want to send the list with mail. Where is my mistake? And How can I fix this?
Tasklistviewcontroller.m
#synthesize tasks = _tasks;
I am transferring from the tasks table view.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *NotDoneCellIdentifier = #"NotDoneTaskCell";
static NSString *DoneCellIdentifier = #"DoneTaskCell";
Task *currentTask = [self.tasks objectAtIndex:indexPath.row];
NSString *cellIdentifier = currentTask.done ? DoneCellIdentifier : NotDoneCellIdentifier;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.textLabel.text = currentTask.name;
return cell;
}
I don't know what is exactly wrong in your code since you don't supply an error and I'm not that proficient yet in Objective-C.
I suspect it is because you reference to "_tasks" which I don't see other code for creating that Class.
NSString *message = [_tasks addObject:task];
Another problem is that you're using task as input object for the array, but it probably doesn't contains something.
You probably wan't something like this:
Task *task = [[Task alloc] initWithName:#"Task 1"];
NSString *message = [[NSString alloc] initWithFormat:#"Task name is %#", task.name];
Also I'm guessing you haven't posted your complete code.
You also forgot to include the right framework for mailing in-app in your header file:
#import <MessageUI/MFMailComposeViewController.h>
Don't forget to also add the framework it to your project!
By the way, you can remove the two lines with synthesize, the compiler does this automatically these days. Nice isn't it?