How to show two objects in one cell (tableView)? - iphone

I ran into a small problem. I created a UITableView where the user can add his contacts from his addressbook. The firstName gets displayed in the first row, the lastName in the second one. I know where the problem is, because right now I actually tell him how to do that.
But how can I tell him to display both names in the same cell? I did it with the following methods but they don't work.
-(void)addObjectsFromArray:(NSArray *)otherArray
{
NSMutableArray *myArray = [[NSMutableArray alloc] init];
[myArray addObjectsFromArray:menuArray];
NSLog(#"%#", myArray);
[self.tableView reloadData];
}
The problem is that I cannot display objects from myArray because cellforRowAtIndex method does not get the value. And with that one I get the same result (firstName first Row, lastName second Row):
NSArray *objects = [[NSArray alloc] initWithObjects:firstName, lastName, nil];
NSString *cellValue = [objects objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
NSLog(#"%#", objects);
return cell;

Not only does your code display the first name and last name in different cells, it also does not work for rows at indexes greater than one. This is because your table data source reads from a brand-new array that you create with alloc/init on the first line, rather than reading from your model. Your little array contains only two objects, explaining the two rows.
Here is what you need to do: first, make an array of users available to your table's data source. Suppose it's called userArray. Put user data for each user into that array. Suppose each user is stored as an object of type MYUser that responds to [user firstName] and [user lastName] calls with the first and the last name of the user.
Now put this code into your table's data source:
MYUser *user = [userArray objectAtIndex:indexPath.row];
NSString *cellValue = [NSString stringWithFormat:#"%# %#", [user firstName], [user lastName]];
cell.textLabel.text = cellValue;
EDIT : If your menuArray stores the first and the last names in separate consecutive string objects, you can still do it (though I recommend against it, because it will be confusing to people who maintain your program in the future).
First, you need to change the method that returns the number of items in the table to return menuArray.count / 2 instead of just menuArray.count. Then you can modify your code as follows:
NSString firstName = [menuArray objectAtIndex:indexPath.row/2];
NSString lastName = [menuArray objectAtIndex:indexPath.row/2 + 1];
NSString *cellValue = [NSString stringWithFormat:#"%# %#", firstName, lastName];
cell.textLabel.text = cellValue;
EDIT2 : Here is how you create a class that stores the first and the last name of a user. You add the #interface part to MYUser.h, and the #implementation part to MYUser.m file. You then import MYUser.h in .m files from which you reference MYUser class, and use initWithFirstName:andLastName: to initialize new instances. The sample below assumes that you use ARC:
MYUser.h file:
#import <UIKit/UIKit.h>
#interface MYUser : NSObject
#property (nonatomic, strong) NSString *firstName;
#property (nonatomic, strong) NSString *lastName;
-(id)initWithFirstName:(NSString*)first andLastName:(NSString*)last;
#end
MYUser.m file:
#implementation MYUser
#synthesize firstName, lastName;
-(id)initWithFirstName:(NSString*)first andLastName:(NSString*)last {
self = [super init];
if (self) {
self.firstName = first;
self.lastName = last;
}
return self;
}
#end

Related

Properties of objects on array (Objective-c)

I have a NSObject class which's name is test.
class test has 3 property. Name, age, id;
I have 3 Objects in test class. s, b, c.
I am putting all of the objects to the array with: NSArray *ary = [NSArray arrayWithObjects:#"a", #"b", #"c", nil];
I am trying to access to the data of property in that array. Which means I have to read, write the property of the object in array in the loop (for loop or while loop).
I found a lot of materials on the internet. The method that I was close to do was:
[[ary objectAtIndex:0] setName:#"example"];
This method was working with setters and getters. But it did give a horrible error. Is there any "WORKING" method to do it?
Thanks...
Let's imagine a Person class:
#interface Person : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic) NSInteger age;
#property (nonatomic) long long identifier;
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age identifier:(long long)identifier;
#end
#implementation Person
+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age identifier:(long long)identifier {
Person *person = [[self alloc] init];
person.name = name;
person.age = age;
person.identifier = identifier;
return person;
}
#end
You can then create an array of people like so:
NSArray *people = #[[Person personWithName:#"Rob" age:32 identifier:2452323],
[Person personWithName:#"Rachel" age:29 identifier:84583435],
[Person personWithName:#"Charlie" age:4 identifier:389433]];
You can then extract an array of people's names like so:
NSArray *names = [people valueForKey:#"name"];
NSLog(#"%#", names);
That will generate:
2013-09-27 14:57:13.791 MyApp[33198:a0b] (
Rob,
Rachel,
Charlie
)
If you want to extract information about the second Person, that would be:
Person *person = people[1];
NSString *name = person.name;
NSInteger age = person.age;
long long identifier = person.identifier;
If you want to change the age of the third person, it would be:
Person *person = people[2];
person.age = 5;
Or, if you want to iterate through the array to extract the information, you can do that, too:
for (Person *person in people) {
NSString *name = person.name;
NSInteger age = person.age;
long long identifier = person.identifier;
// now do whatever you want with name, age, and identifier
}
Try this
STEP 1 : Cast it to the appropriate object type first
s *myS = (s *)[array objectAtIndex:0];
b *myB = (b *)[array objectAtIndex:1];
c *myC = (c *)[array objectAtIndex:2];
STEP 2 : Set / get whatever property you want to
myS.name = #"example";

Getting the id value corresponding to the selectedsegment for segmentControl iphone

First of all sorry for the title.
I have one segment control in my app.The titles of the segments are fetching from the server and setting accordingly(ie YES and NO).
There is also one corresponding id value for each segment.So when the user select YES a corresponding id 8 is to saved,and when user Select NO a corresponding id 5 is to be saved(8 and 5 are coming from the server).
I tried by setting the tags but in vain.
Can anyone please help me with this scenario.
Thanks a lot in advance.
//Code
for(WTMobileDataService_DataAccess_ClsYesNo *temp in YesNo)
{
NSString *s = [NSString stringWithFormat:#"%#",temp.m_YesNoDescription];
ind = [temp.m_PK_YesNoID intValue] - 1;
[items addObject:s];
[serverids addObject:[NSString stringWithFormat:#"%d",ind]];
// [segmentControl setTitle:s forSegmentAtIndex:ind];
}
segmentControl = [[UISegmentedControl alloc]initWithItems:items];
Since each UISegmentedControl has only one tag, you'll need to do something like this:
NSMutableArray *items = [NSMutableArray new];
NSMutableArray *serverIds = [NSMutableArray new];
for (ServerResponseObject *response in serverResponses) {
[items addObject:response.title];
[serverIds addObject:response.id];
}
mySegmentedControl = [[UISegmentedControl alloc] initWithItems:items];
Thereafter, when the user taps on the UISegmentedControl, access the selectedIndex property of the segmented control to find out which segment they tapped on, and use this value as the key to the serverIds array to see what the corresponding ID number is according to the server.
Have two arrays, one with the titles, the other with the values. When the user selects a segment, use the selected segment to return the title, and then use that to get the index of the title. Then you can reference the values array and you've got the corresponding value.
You could also achieve the same thing using an NSDictionary where the title is your key, and the value is... the value.
Something like this:
NSArray titles = #[#"YES", #"NO"];
NSArray values = #[8, 5];
UISegmentedControl control; // Likely defined as a property
if ([control selectedSegmentIndex] != UISegmentedControlNoSegment) {
NSString title = [control titleForSegmentAtIndex:];
int index = [titles indexOfObject:title];
NSString value = [values objectAtIndex:index];
}
I would be tempted to create an object to hold the two values together as they seem like a natural grouping
#interface SegmentInfo : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, assign NSInteger value;
- (instancetype)initWithTitle:(NSString *)title value:(NSInteger)value;
#end
#implementation SegmentInfo
- (instancetype)initWithTitle:(NSString *)title value:(NSInteger)value
{
self = [super init];
if (self) {
_title = [title copy];
_value = value;
}
return self;
}
#end
Now you populate these objects and store them in an array
self.segmentInfos = #[
[[SegmentInfo alloc] initWithTitle:#"Yes" value:8],
[[SegmentInfo alloc] initWithTitle:#"No" value:5],
];
To set up the segmented control you just grab all the titles into an array
some set up method
{
NSArray *segmentTitle = [self.segmentInfo valueForKey:#"title"];
self.segmentControl = [[UISegmentedControl alloc] initWithItems:segmentTitle];
}
Now in your method that handles value changes you simple grab the object at the same index and get the value
- (IBAction)segmentedControlChanged:(UISegmentedControl *)segmentedControl
{
SegmentInfo *selectedSegment = self.segmentInfos[segmentedControl.selectedSegmentIndex];
NSLog(#"title: %#, value: %d", selectedSegment.title, selectedSegment.value);
}

Having problems with Array

SO here's my setup. I have an object called radiostations where I have several strings like callsign, frequency declared and an NSMutableArray called amStationInfo. On my viewcontroller, I access an SQLite database which populates the an array like so...
radiostations.h
#interface radiostations : NSObject {
NSString *format;
NSString *city;
}
#property (nonatomic, retain) NSString *format;
#property (nonatomic, retain) NSString *city;
ViewController.m
radiostations *amStationClass = [[radiostations alloc] init];
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *cityField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 10)];
NSString *formatField = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 0)];
[amStationInfo addObject:amStationClass];
[amStationClass setCity:cityField];
[amStationClass setFormat:formatField];
}
[tabView reloadData];
sqlite3_finalize(statement);
and then I populate a UITableView
NSString *cityValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] city];
NSString *formatValue = [(radiostations *)[amStationInfo objectAtIndex:indexPath.row] format];
cityLabel.text = cityValue;
formatLabel.text = formatValue;
Initially I was dealing with a few Arrays and this worked just fine. I then changed it so that I was only dealing with one array using a class object and now it's not working. I know the SQLite query and what not works so Im not having any problems with that. It seems as though the array does not get populated.
You are changing the properties of the same radiostations object and adding it over and over again to the array. You need to create a new radiostations object for each row from your sqlite database and add this:
while (...) {
// fetch data as before
radiostations *record = [[radiostations alloc] init];
[record setCity: cityField];
[record setFormat: formatField];
[amStationInfo addObject: record];
[record release];
}
If you are using ARC you need to remove the line [record release];, otherwise it is necessary to avoid leaking those objects.
where did you allocate/init your mutablearray?
something like:
NSMutableArray* amStationInfo = [[NSMutableArray alloc] init];
you need to allocate it once, before to add objects in it

iphone listview with several cells

I have been able to make the listview show a single field of data using parts of the code like below.
NSMutableArray *array;
..
..
array = [[NSMutableArray alloc] init];
[array addObject:#"John Doe"];
However I want to keep several fields, like:
Name
ID
Date of Birth
I assume the NSMutableArrary is a NSString but I need something like a struct in C that holds the fields I need.
The ID would be "Hidden" but I need to access it when the user clicks on the line. How I access the ID and the other fields? How do I set this up so the list has the information?
Does anyone have any example code that might explain how to do this?
EDIT #1: Thanks for the comments, but I am too new to iPhone and really need to find example code on how to do this. While the comments make it sound like can do this, I dont know where to start. Can someone post example code for the idea of 3 fields?
EDIT #2: I have tried everything so far, is the the correct way to do this or should I use the ideas below?
Userrec.m
#import "UserRec.h"
#implementation Userrec
#synthesize Name, ID;
-(id)initWithName:(NSString *)n ID:(NSString *)d {
self.Name = n;
self.ID = d;
return self;
}
#end
UserRec.h
#import <UIKit/UIKit.h>
#interface Userrec : NSObject {
NSString *Name;
NSString *ID;
}
#property (nonatomic, retain) NSString *Name;
#property (nonatomic, retain) NSString *ID;
-(id)initWithName:(NSString *)n ID:(NSString *)d;
#end
UserList.m
#synthesize userrecs;
…
- (void)viewDidLoad {
[super viewDidLoad];
NSString *Name = #"Name";
NSString *ID = #"IID";
Userrec *userrec = [[Userrec alloc] initWithName:Name ID:ID ];
[userrecs addObject:userrec];
NSLog(#"Count %d",[userrecs count]);
[userrec release];
NSLog(#"Count %d",[userrecs count]);
}
After I addobject and check the count its = 0. So I assume something is wrong?
NSMutableDictionary is the best way to go. You can do something as follows:
NSMutableDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"John Doe", #"Name", [NSNumber numberWithInt:5], #"ID", nil];
You can keep adding as many fields as you like with that same template, even NSArray objects. I'd look up the documentation if you have any more trouble. Remember, you can only store pointers to objects in an NSDictionary. Things like
NSMutableDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:#"John Doe", #"Name", 5, #"ID", nil];
won't work. Good Luck!
Take a look at an NSMutableDictionary it seems like the exact thing you would want to use
Edit:
Here's some sample code
NSMutableArray *myData = [[NSMutableArray alloc] init];
NSMutableDictionary *myRow = [[NSMutableDictionary alloc] init];
[myRow setObject:#"John Doe" forKey:#"Name"];
[myRow setObject:#"4738" forKey:#"ID"];
[myRow setObject:#"1/23/45" forKey:#"DOB"];
[myData addObject:myRow];
[myRow release];
//Repeat from dictioanry alloc through release for each row you need to add
To display this in a UITableView, you need to have a UITableViewController class. In there override the cellForRowAtIndexPath: function. here is a simple implementation of that function
-(UITableViewCell*) tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = [indexPath row];
static NSString *kCellID = #"cellID";
UITableViewCell *cell = nil;
cell = [tableView dequeueReuseableCellWithIdentifier:kCellID];
if ( cell == nil )
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellID] autorelease];
}
NSMutableDictionary curRow = [myData objectAtIndex:row];
cell.textLabel.text = [curRow objectForKey:#"Name"];
return cell;
}

What's the best way to store and retrieve multi-dimensional NSMutableArrays?

I'm storing a bunch of data in a .plist file (in the application documents folder), and it's structured like this:
Dictionary {
"description" = "String Value",
"sections" = Array (
Array (
Number,
...
Number
),
Array (
Number,
...
Number
)
),
"items" = Array (
Array (
Number,
...
Number
),
Array (
Number,
...
Number
)
)
}
If I just retrieve it with
NSMutableDictionary *d = [[NSMutableDictionary alloc] initWithContentsOfFile:plistFile]
I won't be able to replace the number objects, correct?
So I'm recursing through the data right now and forming a mutable version of the whole thing, and it worked in one instance, but now it's telling me mutating method sent to immutable object when the whole thing is mutable.
Is there an easier/better way to do this? If it makes a difference, my data is just integers and booleans.
Instead of writing all that custom class junk, you should use NSPropertyListSerialization. Specifically, see the propertyListWithData:options:format:error: method. Example usage:
NSMutableDictionary *d = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:#"path/to/file"]
options:NSPropertyListMutableContainers
format:NULL
error:NULL];
This will make all the containers mutable, but keep the leaf nodes (e.g. NSStrings) immutable. There's also an option to make the leaves mutable too.
I usually find it easier to create one or more custom classes to handle loading and saving. This lets you convert the arrays to mutableArrays explicitly:
MyThing.h
#interface MyThing : NSObject
{
NSString * description;
NSMutableArray * sections;
NSMutableArray * items;
}
#property (copy) NSString * description;
#property (readonly) NSMutableArray * sections;
#property (readonly) NSMutableArray * items;
- (void)loadFromFile:(NSString *)path;
- (void)saveToFile:(NSString *)path;
#end
MyThing.m
#implementation MyThing
#synthesize description;
#synthesize sections
#synthesize items;
- (id)init {
if ((self = [super init]) == nil) { return nil; }
sections = [[NSMutableArray alloc] initWithCapacity:0];
items = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc {
[items release];
[sections release];
}
- (void)loadFromFile:(NSString *)path {
NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile:path];
[self setDescription:[dict objectForKey:#"description"]];
[sections removeAllObjects];
[sections addObjectsFromArray:[dict objectForKey:#"sections"]];
[items removeAllObjects];
[items addObjectsFromArray:[dict objectForKey:#"items"]];
}
- (void)saveToFile:(NSString *)path {
NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys:
description, #"description",
sections, #"sections",
items, #"items",
nil];
[dict writeToFile:path atomically:YES];
}
#end;
With that done, you can encapsulate all of the packaging and unpackaging code in your loadFromFile and saveToFile methods. The major benefit of this approach is that your main program gets a lot simpler, and it allows you to access the elements of your data structure as properties:
MyThing * thing = [[MyThing alloc] init];
[thing loadFromFile:#"..."];
...
thing.description = #"new description";
[thing.sections addObject:someObject];
[thing.items removeObjectAtIndex:4];
...
[thing saveToFile:#"..."];
[thing release];
What you want is a deep mutable copy. Cocoa doesn't include a way to do it. A few people have written such deep-copy implementations before (example).
However, Core Foundation includes the CFPropertyList API, which does have support both for creating deep mutable copies of property list objects as well as reading in property lists from disk as mutable datatypes. (And, of course, Core Foundation's property list types are toll-free bridged with Cocoa's, meaning you don't have to convert between them — an NSArray is a CFArray and vice-versa.)