I am new to iOS programming and I need some advice. I want to create a table view with sections to learn how it works. I have some objects from a model class user. I fill data in the table view using dictionaries. The table view is in a view Controller in my storyboard. The code works, but I don't know if this is a good way to handle the data in my table. Is this a semantic mistake to do this like I did?
Please take a Look at my code and give me some advice.
My user object (User.h):
#interface User : NSObject
#property (nonatomic, strong) NSString *email;
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
#property (nonatomic, strong) NSString *city;
#end
Dummy function to fill users into an array (in a helper class):
- (NSMutableArray *) createUser
{
NSMutableArray *userArray = [[NSMutableArray alloc] init];
User *user1 = [[User alloc] init];
user1.firstName = #"Donald";
user1.lastName = #"Duck";
user1.email = #"donald_duck#disney.com";
user1.city = #"Entenhausen";
User *user2 = [[User alloc] init];
user2.firstName = #"Micky";
user2.lastName = #"Maus";
user2.email = #"micky#disney.com";
user2.city = #"Entenhausen";
User *user3 = [[User alloc] init];
user3.firstName = #"Daisy";
user3.lastName = #"Duck";
user3.email = #"daisy_duck#disney.com";
user3.city = #"Frankfurt";
User *user4 = [[User alloc] init];
user4.firstName = #"Goofy";
user4.lastName = #"Goof";
user4.email = #"goofy#disney.com";
user4.city = #"Berlin";
User *user5 = [[User alloc] init];
user5.firstName = #"Some";
user5.lastName = #"Body";
user5.email = #"somebody#disney.com";
user5.city = #"Somewhere";
User *user6 = [[User alloc] init];
user6.firstName = #"Dagobert";
user6.lastName = #"Duck";
user6.email = #"dagobert#disney.com";
user6.city = #"Mainz";
[userArray addObject:user1];
[userArray addObject:user2];
[userArray addObject:user3];
[userArray addObject:user4];
[userArray addObject:user5];
[userArray addObject:user6];
return userArray;
}
Function to create a dictionary object (in a helper class):
- (NSMutableDictionary *) createDictionaryFromArray: (NSMutableArray *) allData
{
NSArray *arrayKeys = [[NSArray alloc] initWithObjects:#"Entenhausen", #"Frankfurt", #"Berlin", #"Mainz", nil];
NSMutableDictionary *collection = [[NSMutableDictionary alloc] init];
for (int i=0; i < arrayKeys.count; i++) {
NSString *key = [arrayKeys objectAtIndex:i];
NSMutableArray *allValues = [[NSMutableArray alloc] init];
for (User *usr in allData) {
if([usr.city isEqualToString:key]) {
[allValues addObject:usr];
}
}
[collection setObject:allValues forKey:key];
}
return collection;
}
My table view controller class (TableViewController.h):
#import <UIKit/UIKit.h>
#import "HelperClass.h"
#interface TableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
HelperClass *helper;
NSMutableArray *users;
NSArray *allKeys;
NSDictionary *dictionaryUsers;
}
TableViewController.m:
#import "TableViewController.h"
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
helper = [[HelperClass alloc] init];
users = [[NSMutableArray alloc] init];
users = [helper createUser];
dictionaryUsers = [[NSMutableDictionary alloc] init];
dictionaryUsers = [helper createDictionaryFromArray:users];
allKeys = [NSArray array];
allKeys = [dictionaryUsers allKeys];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{ // Return the number of sections.
return [allKeys count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSMutableArray *arrayValuesUsers = [[NSMutableArray alloc] init];
arrayValuesUsers = [dictionaryUsers objectForKey:[allKeys objectAtIndex:section]];
return [arrayValuesUsers count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [allKeys objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
User *user = [[User alloc] init];
NSMutableArray *usersInSection = [dictionaryUsers objectForKey:[allKeys objectAtIndex:indexPath.section]];
user = [usersInSection objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#", user.lastName, user.firstName];
cell.detailTextLabel.text = user.email;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *usersInSection = [dictionaryUsers objectForKey:[allKeys objectAtIndex:indexPath.section]];
User *usr = [usersInSection objectAtIndex:indexPath.row];
NSLog(#"user: %#, %#", usr.lastName, usr.firstName);
}
#end
First of all thumbs up for using a HelperClass for implementing general functions. Only when you do it, make it at least a singleton, so that you not always have to call:
helper = [[HelperClass alloc] init];
Or if you only have functions like createUser, these methods should be class methods.
Here are two general hints, which came to my mind, when looking at your code:
You have too many allocations in your code. For example in your TableViewController you do:
users = [[NSMutableArray alloc] init];
users = [helper createUser];
So first you allocate a new array and the first thing the createUser function does is to allocate another array in memory, which is then returned. Your first allocated memory space is never used.
When it comes to tableViews don't use dictionaries. Always stick with arrays. A dictionary is not ordered and when iterating through a dictionary you might get different results each time. An array is ordered and objectOfIndex:i will always return the same object.
So I adjusted your code to have a dynamic tableView sorting your users into sections by their city and sorting them alphabetically. (I guess this was what you wanted to achieve). The new code is not completely clean. You could try to use NSArray instead of NSMutableArray everywhere to save some memory for example.
Your User.h is untouched.
Here is the new HelperClass.h
// HelperClass.h
#import <Foundation/Foundation.h>
#interface HelperClass : NSObject
+ (HelperClass *)sharedHelperClass;
+ (NSMutableArray *) createUser;
+ (NSMutableArray *) getAllCitiesFromUserArray: (NSMutableArray *)userArray;
+ (NSMutableArray *)getUsersOfUsersArray: (NSMutableArray *)userArray fromCity: (NSString *)city;
#end
And the HelperClass.m
// HelperClass.m
#import "HelperClass.h"
#import "User.h"
#implementation HelperClass
+ (HelperClass *)sharedHelperClass
{
//This returns always the same object of HelperClass
static dispatch_once_t pred;
static HelperClass *_sharedHelperClass = nil;
dispatch_once(&pred, ^{ _sharedHelperClass = [[self alloc] init]; });
return _sharedHelperClass;
}
- (id)init{
self = [super init];
if (!self) {
return nil;
}
return self;
}
+ (NSMutableArray *) createUser
{
NSMutableArray *userArray = [[NSMutableArray alloc] init];
User *user1 = [[User alloc] init];
user1.firstName = #"Donald";
user1.lastName = #"Duck";
user1.email = #"donald_duck#disney.com";
user1.city = #"Entenhausen";
User *user2 = [[User alloc] init];
user2.firstName = #"Micky";
user2.lastName = #"Maus";
user2.email = #"micky#disney.com";
user2.city = #"Entenhausen";
User *user3 = [[User alloc] init];
user3.firstName = #"Daisy";
user3.lastName = #"Duck";
user3.email = #"daisy_duck#disney.com";
user3.city = #"Frankfurt";
User *user4 = [[User alloc] init];
user4.firstName = #"Goofy";
user4.lastName = #"Goof";
user4.email = #"goofy#disney.com";
user4.city = #"Berlin";
User *user5 = [[User alloc] init];
user5.firstName = #"Some";
user5.lastName = #"Body";
user5.email = #"somebody#disney.com";
user5.city = #"Somewhere";
User *user6 = [[User alloc] init];
user6.firstName = #"Dagobert";
user6.lastName = #"Duck";
user6.email = #"dagobert#disney.com";
user6.city = #"Mainz";
[userArray addObject:user1];
[userArray addObject:user2];
[userArray addObject:user3];
[userArray addObject:user4];
[userArray addObject:user5];
[userArray addObject:user6];
return userArray;
}
+ (NSMutableArray *) getAllCitiesFromUserArray: (NSMutableArray *)userArray{
NSMutableArray *cityArray = [[NSMutableArray alloc] init];
for (User *user in userArray) {
if (![cityArray containsObject: user.city]){
[cityArray addObject:user.city];
}
}
//Sort the city array alphabetically (localizedCaseInsensitiveCompare even regards Umlaute)
[cityArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
return cityArray;
}
+ (NSMutableArray *)getUsersOfUsersArray: (NSMutableArray *)userArray fromCity: (NSString *)city{
NSMutableArray *usersOfCityArray = [[NSMutableArray alloc] init];
for (User *user in userArray) {
if ([user.city isEqualToString:city]){
[usersOfCityArray addObject:user];
}
}
//Sort the array of custom objects by key lastName
NSSortDescriptor *sortDescriptor =
[NSSortDescriptor sortDescriptorWithKey:#"lastName"
ascending:YES
selector:#selector(localizedCaseInsensitiveCompare:)];
usersOfCityArray = [[usersOfCityArray sortedArrayUsingDescriptors:#[sortDescriptor]]mutableCopy];
return usersOfCityArray;
}
#end
The new TableViewController.h
// TableViewController.h
#import <UIKit/UIKit.h>
#interface TableViewController : UITableViewController
#end
And the new TableViewController.m
// TableViewController.m
#import "TableViewController.h"
#import "HelperClass.h"
#import "User.h"
#interface TableViewController ()
#property (nonatomic,strong) NSMutableArray *users;
#property (nonatomic,strong) NSMutableArray *sectionHeaders;
#end
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//This gets the Array of Users from the HelperClass class method
//You could access the HelperClass Singleton with [HelperClass sharedHelperClass], but this is not needed in this case.
self.users = [HelperClass createUser];
self.sectionHeaders = [HelperClass getAllCitiesFromUserArray:self.users];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.sectionHeaders count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//Get the current section city
NSString *city = [self.sectionHeaders objectAtIndex:section];
NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];
return sectionUsers.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.sectionHeaders objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
//Get the current section city
NSString *city = [self.sectionHeaders objectAtIndex:indexPath.section];
//Get users for the current city
NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];
//Get the user for the current cell
User *user = [sectionUsers objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#, %#", user.lastName, user.firstName];
cell.detailTextLabel.text = user.email;
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Get the current section city
NSString *city = [self.sectionHeaders objectAtIndex:indexPath.section];
//Get users for the current city
NSMutableArray *sectionUsers =[HelperClass getUsersOfUsersArray:self.users fromCity:city];
//Get the user for the current cell
User *user = [sectionUsers objectAtIndex:indexPath.row];
NSLog(#"user: %#, %#", user.lastName, user.firstName);
}
#end
Related
I'm building an app (UITabBar) where I'm storing a NSMutableArray of custom objects. My custom object is called DayModel.
My DayModel.h file:
#import <Foundation/Foundation.h>
#interface DayModel : NSObject
#property (nonatomic, retain) NSDate *mydate;
#property (nonatomic) float myFloat;
#end
My DayModel.m file:
#import "DayModel.h"
#implementation DayModel
#synthesize myDate, myFloat;
-(id)init {
// Init self
self = [super init];
if (self) {
// Setup
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:self.myDate forKey:#"myDate"];
[coder encodeObject:self.myFloat forKey:#"myFloat"];
}
- (id)initWithCoder:(NSCoder *)coder;
{
self = [[DayModel alloc] init];
if (self != nil)
{
self.myDate = [coder decodeObjectForKey:#"myDate"];
self.myFloat = [coder decodeFloatForKey:#"myFloat"];
}
return self;
}
#end
The "main" ViewController, saving the new objects:
// Add data to the DayModel class
DayModel *currentDay = [[DayModel alloc] init];
currentDay.myDate = myDate;
currentDay.myFloat = myFloat;
// Add currentDay to the _objects NSMutableArray
[_objects insertObject:currentDay atIndex:0];
// Save this array using NSKeyedArchiver
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:#"objects"];
my UITableViewController displaying this:
viewWillAppear
// Load the _objects array
NSData *objectsData = [defaults objectForKey:#"objects"];
if (objectsData != nil)
{
NSArray *oldArray = [NSKeyedUnarchiver unarchiveObjectWithData:objectsData];
if (oldArray != nil)
{
_objects = [[NSMutableArray alloc] initWithArray:oldArray];
} else
{
_objects = [[NSMutableArray alloc] init];
}
} else
{
_objects = [[NSMutableArray alloc] init];
}
Other methods:
- (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 [_objects count];
}
Loading the data:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
// Get the DayModel
DayModel *currentModel = [[DayModel alloc] init];
currentModel = _objects[indexPath.row];
// Get the UILabels
UILabel *dateLabel = (UILabel *)[cell viewWithTag:10];
UILabel *floatLabel = (UILabel *)[cell viewWithTag:20];
// Create the DateFormatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
// Set the text
dateLabel.text = [dateFormatter stringFromDate:currentModel.myDate];
floatLabel.text = [NSString stringWithFormat:#"%.02f", currentModel.myFloat];
return cell;
}
Reproducing the problem:
Add item from tab nr 1
Go to tab nr 2 (table). Data is displayed correctly
Go to tab nr 1 and add new object
Go to tab nr 2 (table). New item is displayed with data from previews item, not new data.
When the app is reloaded, the table displays correctly.
EDIT
What happens is that the new item is added at index 0 to appear at the top of the list, while the tableview class get's the new information from the last row when it should be getting it from the top. How can I "reverse" this?
Thanks!
Erik
I fixed it by reloading the whole list, not only adding the new item. This code is going under the lines loading the list from NSUserDefaults (viewWillAppear).
// Reload the UITableView completely
[self.tableView reloadData];
Please tell me if there's a better solution:)
I've got a problem with my TableViewController. I'm posting 3 values to the tableViewController. A scoreID, a scoreDate, and a scoreValue. It all seems to work correctly, but my problem is, that the scoreValue is offset by 1 to the scoreID and Date. It's easier to show with a picture:
("A good business idea" Should have the value of "another good idea" and so on and and so on)
Now, I know from looking in the .sqlite database file, that the scores are offset by one. So my question is how to fix it?
My tableViewController.m looks like this:
#import "CEWTableViewCell.h"
#interface CEWScoreTableViewController ()
#property (nonatomic) NSInteger countOfRows;
#property (nonatomic, strong) NSMutableArray *scoreIDsForTableCells;
#property (nonatomic, strong) NSArray *fetchedObjects;
#property (nonatomic, strong) NSDictionary *getScoreDataToDict;
#end
#implementation CEWScoreTableViewController{
UITableView *tableView;
}
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat topLayoutGuide = self.topLayoutGuide.length;
tableView.contentInset = UIEdgeInsetsMake(topLayoutGuide, 0, 0, 0);
tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tableView.delegate = self;
tableView.dataSource = self;
[self.tableView registerClass:[CEWTableViewCell class] forCellReuseIdentifier:#"CellID"];
tableView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:tableView];
//INITIALISE APPDELEGATE AND CREATE INSTANCE. MAKE FETCH REQUEST AND POPULATE DATA TO NSDICTIONARY
CEWAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
fetchRequest.resultType = NSDictionaryResultType;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ScoreData" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error = nil;
self.fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
if (fetchRequest == nil) {
NSLog(#"an error occured fetching objects...!");
}
NSLog(#"objectForKey returns: %#", self.scoreIDFromEntity);
NSUInteger countIDsForRows = [self.fetchedObjects count];
self.countOfRows = countIDsForRows;
}
- (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.
//This value is set to the number of #"scoreID"s returned
return self.countOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellID";
CEWTableViewCell *cell = (CEWTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[CEWTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
self.getIndexPath = indexPath.row;
NSDictionary *getScoreIDs = [self.fetchedObjects objectAtIndex:[indexPath row]];
NSDictionary *getDates = [self.fetchedObjects objectAtIndex:[indexPath row]];
//This is just formatting the date to the format I want displayed
NSDate *pullDates = [getDates valueForKey:#"scoreDate"];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"dd-MMM-yyyy HH:MM"];
NSString *formattedDate = [dateFormat stringFromDate:pullDates];
//Here I fetch the #"scoreValue"s and convert them from a string, so I can add them up
NSDictionary *getScoreValues = [self.fetchedObjects objectAtIndex:[indexPath row]];
NSString *convertDictKeyToString = [getScoreValues objectForKey:#"scoreValue"];
NSArray *items = [convertDictKeyToString componentsSeparatedByString:#","];
double total = 0.0;
for (NSString *string in items)
{
total += [string floatValue];
}
cell.descriptionLabel.text = [getScoreIDs objectForKey:#"scoreID"];
cell.dateLabel.text = formattedDate;
cell.tableCellScoreLabel.text = [NSString stringWithFormat:#"%1.1f", total];
return cell;
}
I hope this is enough code, otherwise let me know, and I will try and provide more
ANSWER
Sooooooo. It turns out, that my labels was offset because of the CGRect x,y,x,y values.... Ehrm...
In my experience cellForRowAtIndexPath gets called really fast so it doesn't have a lot of time to perform calculations, and for what I've read formatters take more resources than what you think.
I usually solve this issue by creating a new method that gets called from viewDidLoad or viewWillAppear, and in this method I create an array or some sort of container for the data I need.
Then cellForRowAtIndexPath only has to call the array by indexes.
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
there is a strange problem I have not met ever
there is an array() including some custom object named MyClass parsed by JSONKit;
when I keep scrolling the tableview the memory will keeping increasing too.
but when replace
cell.textLabel.text = myclass.name;
with
cell.textLabel.text = #"cool";
or
cell.textLabel.text = [NSString stringWithFormate:#"a-%d", indexPath.row];
it's ok the memory with keep stable
but if I use
cell.textLabel.text = [NSString stringWithFormate:#"a-%#-i",myclass.name, indexPath.row];
it also keep increasing;
It will drive my crazy!!
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"Singers";
OMTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
MyClass *myclass = [self.data objectAtIndex:indexPath.row];
if (cell == nil){
cell = [[[OMTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = myclass.name;
return cell;
}
MyClass
there is two class one Base another inherit
Base:
#interface OMBase : NSObject {
NSMutableDictionary *data;
NSString *name;
NSArray *keys;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, copy) NSMutableDictionary *data;
#implementation OMBase
#synthesize data, name;
- (void)setData:(NSMutableDictionary *)adata{
if (data){
[data release];
data = nil;
}
data = [adata mutableCopy];
}
- (void)dealloc{
if (keys){
[keys release];
}
[data release];
[super dealloc];
}
- (id)init{
if (self = [super init]){
self.data = [[[NSMutableDictionary alloc] initWithCapacity:20] autorelease];
}
return self;
}
inherit:
#import "OMBase.h"
#interface OMLyric : OMBase
- (NSString *)songid;
- (NSString *)content;
#import "OMLyric.h"
#implementation OMLyric
- (NSString *)songid{
return [data objectForKey:#"songid"];
}
- (NSString *)content{
return [data objectForKey:#"content"];
}
Seems like your myclass.name getter returns a new allocated object. We can't say more without seeing myclass.
If one had a NSString that needed a userid to be used as a URL for a request:
And one had a NSMutableArray that he wanted to Queue into the above call one at a time? So basically make 3 calls of NSString from the NSMutableArray .
One can check multiple UITableView cells and once completed I can index which cell rows were pushed. That is what userIDArray is used for now I want to make a call with the userID's I got back from userIDArray.
for (NSDictionary* userIDDict in userIDArray)
{
userIDArray = [[NSMutableArray alloc] init]; //I put this line in my viewdidload
NSNumber* userID = [userIDDict objectForKey:#"UserID"];
}
UserIDArray is the NSMutableArray .
This would be the NSLog from the NSMutableArray The Integer would be 1, 2 and 3.
UserID: 1
UserID: 2
UserID: 3
So in other words I would like to take the results from my NSMultiTableArray 1,2 and 3 to use within the NSString :
NSString *userProfile = [NSString stringWithFormat:#"http://example.com/userid=1"];
NSString *userProfile = [NSString stringWithFormat:#"http://example.com/userid=2"];
NSString *userProfile = [NSString stringWithFormat:#"http://example.com/userid=3"];
So I would make the first call and wait for a result, and then the second and finally the third.
Can this be done? I have search this link about Queues and this one but I am unsure if those are what I need?
UserDetailViewController.h file:
#interface UserDetailViewController : UIViewController <UITableViewDelegate>{
long long expectedLength;
long long currentLength;
UITableView *userTableView;
NSIndexPath* checkedIndexPath;
}
#property (nonatomic, retain) NSArray *userIDJson;
#property (strong, nonatomic) NSDictionary *userIDDict;
#property (nonatomic, retain) NSIndexPath* checkedIndexPath;
#property (nonatomic, strong) NSMutableArray *userIDArray;
#property (nonatomic) NSInteger currentUserIndex;
#end
UserDetailViewController.m file:
#interface UserDetailViewController ()
#end
#implementation UserDetailViewController
#synthesize userIDJson;
#synthesize userIDDict;
#synthesize checkedIndexPath;
#synthesize userIDArray;
#synthesize currentUserIndex;
- (void)viewDidLoad
{
[super viewDidLoad];
userIDArray = [[NSMutableArray alloc] init];
[self.navigationController setNavigationBarHidden:NO];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
//return self.loadedSearches.count;
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.userIDJson.count;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
}
cell.textLabel.text = self.userIDJson[indexPath.row][#"UserName"];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if([self.checkedIndexPath isEqual:indexPath])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *thisCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *userStringIndex = [self.userIDJson objectAtIndex:indexPath.row];
if (thisCell.accessoryType == UITableViewCellAccessoryNone)
{
thisCell.accessoryType = UITableViewCellAccessoryCheckmark;
[userIDArray addObject:userStringIndex];
}
else
{
thisCell.accessoryType = UITableViewCellAccessoryNone;
[userIDArray removeObject:userStringIndex];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.currentUserIndex < userIDArray.count) {
NSNumber* userID = [[userIDArray objectForIndex:currentUserIndex]objectForKey:#"UserID"];
//Make the actual request here, and assign the delegate.
NSString *userProfile = [NSString stringWithFormat:#"http://example.com/userid=%#",userID];
self.currentUserIndex++;
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:userProfile]];
NSString *userResult = [[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding];
NSURL *url = [[NSURL alloc] initWithString: userProfile];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
userIDJson = [NSJSONSerialization JSONObjectWithData:dataURL
options:kNilOptions
error:&error];
}
}
for (userIDDict in userIDArray)
{
NSNumber* userID = [userIDDict objectForKey:#"UserID"];
NSLog(#"%#", userID);
NSArray* userName = [userIDDict objectForKey:#"UserName"];
}
NSURLConnection can take a delegate through the constructor initWithRequest:delegate:. So you need the object that makes the calls conform to that protocol, I'll assume it's a UIViewController. You can use one of the required methods in the delegate to fire up the next request.
For example, assume you have property to indicate the current index.
#property (nonatomic) NSInteger currentUserIndex;
Then in the place that will fire the first request, make the call for the first user. In some delegate method, say connectionDidFinishLoading:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (self.currentUserIndex < self.userIDArray.count) {
NSNumber* userID = [[self.userIDArray objectAtIndex:self.currentUserIndex] objectForKey:#"UserID"];
self.currentUserIndex++;
//Make the actual request here, and assign the delegate.
}
}
Of course, if your connection calls don't have to be synchronous, you can do it in an easier way.