In my iPhone app I want to show a table view with section headers.
I have a datasource like this(Example)
(
{date : 12-10-2014 rowdata:abc},
{date : 12-10-2014 rowdata:pqr},
{date : 12-10-2014 rowdata:xyz},
{date : 13-10-2014 rowdata:123},
{date : 13-10-2014 rowdata:780},
{date : 14-10-2014 rowdata:tuv},
)
I want to show the section header as the date - and have to show row data in it like this
(just consider it as a format of table view-dates are it header views and below rows)
12-10-2014
abc
pqr
xyz
13-10-2014
123
780
13-10-2014
tuv
please give me a logic to do how to change or create new arrays with my data source, I am clear working with sectioned tableview and header views.
i can create a seperate array with dates for header view, but how can I show rowdata under every section, because in every section row starts with index 0.
You are basically asking how to categorise your rowdata data by its date key.
Simple. You need to do the following:
Create data models for your data couplets so that its nicer to populate your table with and store in arrays :)
Create a dictionary that will store the date as keys - you can imagine them as categories - and the row data as objects stored in an array for each category.
Step 1:
Create your custom data model to make data management more easier and cleaner.
Create two new files called rowDataModel.h and rowDataModel.m. They will be a subclass of the NSObject class.
This is what your .h file would look like:
#import <Foundation/Foundation.h>
#interface rowDataModel : NSObject
#property (nonatomic, retain) NSString * rowDataDate;
#property (nonatomic, retain) NSString * rowDataInformation;
-(id)initWithJSONData:(NSDictionary*)data;
#end
and in your .m file you will have this:
#import "rowDataModel.h"
#implementation rowDataModel
#synthesize rowDataDate;
#synthesize rowDataInformation;
-(id)initWithJSONData:(NSDictionary*)data{
if (self = [super init]) {
self.rowDataDate = [data objectForKey:#"date"];
self.rowDataInformation = [data objectForKey:#"rowdata"];
}
return self;
}
#end
We are doing this as this will contain easy to work with containers. Its a good way to manage your custom json objects too.
Step 2
Now in your view controller where you will be populating the database and categorising your data, make sure you import your custom rowDataModel.h file
NSMutableDictionary *myData = [[NSMutableDictionary alloc] init];
//Have a for loop that iterates through your array
for(NSDictionary *currentItem in serverArray){
RowData *newItem = [[RowData alloc] initWithJSONData:currentItem];
//If category the date key does not exist, then create a new category for it
if([myData objectForKey:newItem.rowDataDate] == nil){
//We want to create an array for each category that will hold all the row datas
[myDate setObject:[NSMutableArray new] forKey:newItem.rowDataDate];
}
//Grab the array from a particular category and add the current item to this category
[[myDate objectForKey:newItem.rowDataDate] addObject:newItem];
}
Now when you are populating your table delegate methods, in your titleForHeader tableview delegate methods, you simply grab the category titles: the dates from your myData dictionary using the allKeys property, and call the `objectForIndex: method for the section counter in your delegate method, ad you use that string to set the title of your header view by using the titleForHeader table view delegate method.
And in your cellForRowAtIndexPath: delegate method, you simply grab the correct category in your myData array by using the keys, and then access the individual objects in the array based on the row count.
And thats all bro. Goodluck
disclaimer, I have not tested this code, this should work right off the bat :)
The best way to do this would be creating a custom object, let's call it MyDate. My date would have two properties: a date, and an array.
#interface MyDate
#property NSDate *date;
#property NSArray *items;
#end
You parse the string, and for each information bit, you check the date. If the date is not in the array you add it. Then you add the other string to the items array for the appropriate MyDate object.
This way, every object in items starts with index 0, just like the rows in each section. So managing your model this way makes it easy to handle the user interface.
You could do this with arrays and dictionaries but it get's a little messy and you are probably best of creating new objects that can handle this.
I would probably start with something that looks a little like this
#interface NMKSection : NSObject
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSArray *rows;
#end
#interface NMKDataSource : NSObject
#property (nonatomic, strong) NSArray *data;
#property (nonatomic, strong) NSArray *sectionSortDescriptors;
#property (nonatomic, strong) NSArray *rowSortDescriptors;
- (NMKSection *)sectionAtIndex:(NSInteger)index;
- (void)processData;
#end
This would then mean that your UITableViewDataSource can look very simple
- (void)methodToSetupDataSource
{
self.dataSource = [[NMKDataSource alloc] init];
self.dataSource.data = data
self.dataSource.sectionSortDescriptors = // optional;
self.dataSource.rowSortDescriptors = // optional;
[self processData];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NMKSection *section = [self.dataSource sectionAtIndex:section];
return [self.dataFormatter stringFromData:section.date];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = // get cell
id row = [self.dataSource sectionAtIndex:indexPath.section].rows[indexPath.row];
// configure cell
return cell;
}
To achieve this all the heavy lifting is hidden in the NMKDataSource. The implementation fo NMKSection can just be left blank as it will contain no logic
#implementation NMKSection
#end
All the hard work is started from processData
#interface NMKDataSource ()
#property (nonatomic, strong) NSArray *sections;
#end
#implementation NMKDataSource
- (id)init;
{
self = [super init];
if (self) {
_sections = [[NSMutableArray alloc] init];
}
return self;
}
- (NMKSection *)sectionAtIndex:(NSInteger)index;
{
return self.sections[index];
}
- (void)processData
{
if (self.sectionSortDescriptors) {
self.sections = [self.unsortedSections sortedArrayUsingDescriptors:self.sectionSortDescriptors];
} else {
self.sections = [self.unsortedSections copy];
}
}
- (NSDictionary *)groupedData;
{
NSMutableDictionary *groupedData = [[NSMutableDictionary alloc] init];
[self.data enumerateObjectsUsingBlock:^(NSDictionary *row, NSUInteger idx, BOOL *stop) {
NSString *key = row[#"date"];
NSMutableArray *rows = groupedData[key];
if (!rows) {
groupedData[key] = rows = [[NSMutableArray alloc] init];
}
[rows addObject:row[#"rowdata"]];
}];
return groupedData;
}
- (NSArray *)unsortedSections;
{
NSMutableArray *unsortedSections = [[NSMutableArray alloc] init];
[self.groupedData enumerateKeysAndObjectsUsingBlock:^(NSDate *date, NSArray *rows, BOOL *stop) {
[unsortedSections addObject:({
NMKSection *section = [[NMKSection alloc] init];
section.date = date;
section.rows = rows;
if (self.rowSortDescriptors) {
section.rows = [rows sortedArrayUsingDescriptors:self.rowSortDescriptors];
}
section;
})];
}];
return [unsortedSections copy];
}
#end
This was written in the browser so it's not been tested. The code it just an example of where I would probably start and develop this further until I was happy
Related
Super basic questions, which i'm having problems with.
How do i add multiple objects to my NSMutableArray? (Now i only add one with self.itemsArray[0] = iPhoneItem; )
How do i retrieve for the first objects property (itemName)?
I have a calss: Item - which looks like follows.
Item.h
#interface Item : UITableViewController
#property (nonatomic, copy) NSString *itemTitle;
- (id)initWithItemTitle:(NSString *)aTitle;
#end
Item.m
#interface Item ()
#end
#implementation Item
- (id)initWithTitle:(NSString *)aTitle {
self = [super init];
if (self) {
self.itemTitle = aTitle;
}
return self;
}
#end
And now i just want to create a few objects, add them in to an NSMutableArray and retrieve the itemTitle property.
ViewController.m - (.h has no additional changes from standard "create singel view application"
#import "ViewController.h"
#import "Item.h"
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *itemsArray;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Item *iPhoneItem = [[Item alloc] initWithItemTitle:#"iPhone"];
Item *iPadItem = [[Item alloc] initWithItemTitle:#"iPad"];
Item *macBookPro = [[Item alloc] initWithItemTitle:#"MacBookPro"];
self.itemsArray[0] = iPhoneItem;
NSLog(#"%#", self.itemsArray[0].itemTitle); //How would i do this?
}
#end
Best regards, iOS-rookie.
You can simply check iOS reference, no need to ask questions: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableArray/addObjectsFromArray:
Anyway, here is an example:
[itemsArray addObjectsFromArray: #[obj1, obj2]]; //adding multiple objects
((Item *)[itemsArray firstObject]).itemTitle //get title of your object
It is bad practice to access properties for an object in an array directly like :
self.itemsArray[0].itemTitle
It is cleaner to:
Item iPhoneItem = (Item)[itemsArray objectAtIndex:0];
NSLog(#"%#", iPhoneItem.itemTitle);
Also keep in mind that you can use [itemsArray firstObject]; and [itemsArray lastObject];
Im developing an iOs application and i have an array of objects each object has a name, id, amd describtion. i want to display the name in the table view cell and the description in the detailview controller.
I know there are plenty of tutorial it wouldnt work for my case i think.
Where should implement the array in which class the view controller or the class model or the array class model.
May be you can start to hard init your array of objects in your tableViewController viewDidLoad.
#import "TableViewController.h"
#interface TableViewController ()
#property (strong, nonatomic) NSMutableArray *myData;
#end
#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.myData = [[NSMutableArray alloc]init];
YourObectClass *myObject;
for (int i = 0; i < 20; i++) {
myObject = [[YourObectClass alloc] init];
myObject.name = [NSString stringWithFormat:#"Object:%d",i];
[self.myData addObject:myObject];
]
//here you are... now use self.myData as your data source...
}
....
#end
I have a shared singleton classNSMutableArray [ICGlobals sharedApplianceCount](first time using this pattern so bear with me if ive done something really silly here)
.h
#import <Foundation/Foundation.h>
#interface ICGlobals : NSObject
{
NSMutableArray* applianceCount;
}
#property (nonatomic, retain) NSString *applianceCount;
+ (ICGlobals *)sharedApplianceCount;
#end
.m
#import "ICGlobals.h"
#implementation ICGlobals
static ICGlobals *sharedApplianceCount = nil;
+ (ICGlobals *)sharedUser {
if(sharedApplianceCount == nil){
sharedApplianceCount = [[super allocWithZone:NULL] init];
}
return sharedApplianceCount;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedApplianceCount];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
In "another view controller" im trying to add the row count of my table view (changeable amount of rows) = self.circuits.count
Having tried this
[[ICGlobals sharedApplianceCount] addObject: self.circuits.count,nil]];
and
[[ICGlobals sharedApplianceCount] = [[NSMutableArray alloc] init];
[[ICGlobals sharedApplianceCount] addObject: self.circuits.count,Nil]];
I get no visible #interface error saying my singleton class declares the selector
same with
NSNumber* numberOfRows = [NSNumber numberWithInteger:self.circuits.count];
[[ICGlobals sharedApplianceCount]addObject:[NSMutableArray arrayWithObjects:numberOfRows, nil]];
and with
[ICGlobals sharedApplianceCount] = self.circuits.count;
I get expression assignable. Singleton class has been imported.
You have an inconsistency in your interface declaration. You declare ivar of type NSMutableArray and then a NSString property. Firstable, you don't need to declare ivar, declaring a property does it for you. So your interface should look like:
#interface ICGlobals : NSObject
#property (nonatomic, retain) NSMutableArray *applianceCount;
+ (ICGlobals *)sharedApplianceCount;
#end
Furthermore, you have a naming glitch. You should not use name applianceCount for an array. In general, naming convention of Cocoa suggests that count should be a number (int or NSUInteger). I would change this property name to applianceCounts.
Then, when you initialize your singletone, you can also initialize the array:
+ (ICGlobals *)sharedUser
{
if(sharedApplianceCount == nil)
{
sharedApplianceCount = [[super allocWithZone:NULL] init];
sharedApplianceCount.applianceCounts = [[NSMutableArray alloc] init];
}
return sharedApplianceCount;
}
Finally, here is how to add data to your singletone's applianceCounts array from view controller.
NSNumber* numberOfRows = [NSNumber numberWithInteger:self.circuits.count];
[[ICGlobals sharedApplianceCount].applianceCounts addObject:numberOfRows];
This should point you to right direction.
I don't fully get what you are trying to achieve like I don't understand why you want to have an array there, so if you need further help please let me know in the comments.
I fully recommend you reading about naming conventions. A good start is this article:
Introduction to Coding Guidelines for Cocoa.
I would recommend some refactoring to your class.
First you make the interface like this:
#interface ICGlobals : NSObject
// add the app count but make it private, because you will provide methods to access it
#property (nonatomic, readonly) NSString *applianceCount;
// return ICGlobals instance
+ (ICGlobals)sharedCounter;
- (NSInteger)count;
- (void)addObject:(id)object;
now in .m file
#implementation ICGlobals
static ICGlobals *sharedApplianceCount = nil;
// this is your method, just changed the name
+ (ICGlobals *)sharedCounter {
if(sharedApplianceCount == nil){
sharedApplianceCount = [[super allocWithZone:NULL] init];
}
return sharedApplianceCount;
}
// instance methods goes here
- (NSInteger)count
{
return _applicationCount.count;
}
- (void)addObject:(id)object
{
[_applicationCount addObject:object];
}
Now call [[ICGlobals sharedCount]addObject:object] from any viewController
I have an NSArray of songs fetched from a server. That is my raw data source. The array contains custom objects which have an NSDictionary and NSArray as backend.
I am wondering if implementing a formatted data source using an NSDictionary will be wise. The dictionary will have section headers as keys, and the value of a certain key will have an NSArray containing the rows for that section.
I will be iterating through my raw data source and arranging it alphabetically into the dictionary.
I have a feeling that this is not a solid implementation and is very expensive. Is there any other, more solid implementation than this?
For small tables, rather than NSDictionary, I generally use NSArray, since dictionaries don't preserve order (and you probably don't want to continually re-sort). So I usually have an array of sections, for which I have for each section entry, at the very least, a section title and an array of rows. My array of rows has, that information that I need to present a given row (e.g. the text of the row, etc.).
The individual row and section objects, you can implement those as a NSDictionary objects themselves (and sometimes when parsing the data from JSON or XML, that's easiest), but I generally define my own Row and Section objects, e.g.:
#interface Row : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *subtitle;
#end
and
#interface Section : NSObject
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSMutableArray *rows;
#end
Then my table view controller has an NSArray for the sections:
#property (nonatomic, strong) NSMutableArray *sections;
And I populate it like so:
self.sections = [NSMutableArray array];
Section *sectionObject;
sectionObject = [[Section alloc] initWithTitle:#"Marx Brothers" rows:nil];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Chico" subtitle:#"Leonard Marx"]];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Harpo" subtitle:#"Adolph Marx"]];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Groucho" subtitle:#"Julius Henry Marx"]];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Zeppo" subtitle:#"Herbert Manfred Marx"]];
[self.sections addObject:sectionObject];
sectionObject = [[Section alloc] initWithTitle:#"Three Stooges" rows:nil];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Moe" subtitle:#"Moses Harry Horwitz"]];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Larry" subtitle:#"Louis Feinberg"]];
[sectionObject.rows addObject:[[Row alloc] initWithTitle:#"Curly" subtitle:#"Jerome Lester \"Jerry\" Horwitz"]];
[self.sections addObject:sectionObject];
And then I have the typical UITableViewDataSource methods:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.sections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Section *sectionObject = self.sections[section];
return [sectionObject.rows count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
Section *sectionObject = self.sections[section];
return sectionObject.title;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
Section *sectionObject = self.sections[indexPath.section];
Row *rowObject = sectionObject.rows[indexPath.row];
cell.textLabel.text = rowObject.title;
cell.detailTextLabel.text = rowObject.subtitle;
return cell;
}
For bigger, database data driven tables, I might not keep the data in arrays, but rather use Core Data or SQLite, but the idea is the same. Make sure I have Section and Row classes that keep my table view controller code self-explanatory and insulated from the details of the data implementation.
Have you tried RestKit? All you need is to provide source of json encoded objects and create model classes
You can use a model class and then create objects of this model class with the data from the server. Then use the array of model class objects as a source to your UITableView. You could also consider core-data , in which its very easy to create data models and relations. However it comes with a lot of additional features which might be extra baggage for you.
Goal of program: Enter numbers on a viewController. When the user hits Submit button, the data entered by user is passed on to a different class for displaying on a different viewController.
Problem: I am trying to access an instance variable (numberList) in an instance method (-(void)insertNewNumber:(Numbers *)tempNumber), but it never gives me the correct output. But when I access the same variable through a protocol method of UITableViewDataSource, I get the correct answer. I figured this by using NSLog in instance method and protocol method.
Since I have declared numberList as a property variable, I was thought that I can access it from anywhere in the program and get the correct value stored in it. But compiler returned 0 for the NSLog statements when they were called from instance method. When the NSLog statements from protocol method, showed the correct result.
Please help me understand why is this occurring and how can I add elements into an array from any method in a program.
Thank you!
Here's the relevant code I am working on:
Numbers.h:
#interface Numbers:NSObject
#property (strong, retain) NSString *ID;
#property (strong, retain) NSInteger *number;
#end
Numbers.m
#implementation Numbers
#synthesize ID, number;
#end
DisplayNumbers.h
#import <UIKit/UIKit.h>
#import "Numbers.h"
#interface DisplayNumbers : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSMutableArray *numberList;
- (void)insertNewNumber:(Numbers *)tempNumber;
#end
DisplayNumbers.m:
#implementation DisplayNumbers
#synthesize numberList;
- (void)viewDidLoad
{
[super viewDidLoad];
numberList = [[NSMutableArray alloc] init];
Numbers *num0 = [[Numbers alloc] init];
Numbers *num1 = [[Numbers alloc] init];
num0.ID = [NSString stringWithFormat:#"ID 0"];
num0.number = 1111111111;
num1.ID = [NSString stringWithFormat:#"ID 1"];
num0.number = 2222222222;
[numberList addObject:num0];
[numberList addObject:num1];
}
- (void)insertNewNumber:(Numbers *)tempNumber
{
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
[numberList addObject:tempNumber];
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[numberList addObject:temp];
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
return numberList.count;
}
#end
Edit 1: Calling of insertNewNumber:.
This method is being called from a different class.
InputNumber.h:
#import <UIKit/UIKit.h>
#import "DisplayNumbers.h"
#interface InputNumber:UIViewController
#property (retain, strong) NSInteger *enteredNumber;
-(void)enteredNumber;
#end
InputNumber.m
#implementation InputNumber
#synthesize enteredNumber;
-(void)enterNumber
{
DisplayNumber *temp = [[DisplayNumber alloc] init];
[temp insertNewNumber:enteredNumber];
}
#end
Since you allocate your numberList in the ViewDidLoad method, be sure to call your insertNewNumber method after the call to ViewDidLoad.
I believe that
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[self insertNewNumber:temp];
return contactList.count;
}
works, right?
If you need to call your insertNewNumber method before the call to ViewDidLoad, allocate your numberList
numberList = [[NSMutableArray alloc] init];
in an overloaded initWithNibName:bundle: method.
Your code doesn't have an ivar called numberList. You need to call the property like this:
self.numberList = [NSMutableArray array];
No need to alloc/init since you're already using a strong reference.
Every time you refer to that numberList object, you need to use self.numberList.
[self.numberList addObject:num0];
[self.numberList addObject:num1];