I am trying to integrate the AddressBookUI API into my iOS 5 app to display selected content in a table view. I have been able to implement the AddressBook Picker and set it so when a user selects a person from their address book, it populates the label of the cell in the TableView. I would also like it to be able to display the image of the selected person in the same cell if one exists and a default missing picture image if there is not one.
I can't see to get my head around how to store both the name and the image data in the same TableView cell.
Anyone have any suggestions of how I might accomplish this. I have read the developer docs and know i should be using the ABPersonCopyImageDataWithFormat command. I just can't seem to get it to implement into the tableview cell.
Here are the snippets of code I have so far:
// Store the name into memory when the user selects, then dismiss the view.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
NSString *selectedPerson = (__bridge NSString *)ABRecordCopyCompositeName(person);
[people insertObject:selectedPerson atIndex:0];
if (ABPersonHasImageData(person) == TRUE) {
NSLog(#"Person has an image!");
} else {
NSLog(#"Person does not have an image.");
}
[self dismissModalViewControllerAnimated:YES];
[self.tableView reloadData];
return NO;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
PartyCell *cell = (PartyCell *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
cell.backgroundView = [[GradientView alloc] init];
cell.personLabel.text = [people objectAtIndex:indexPath.row];
cell.personImage.image = [UIImage imageNamed:#"missing.png"]; // THIS NEEDS TO DISPLAY THE SELECTED USERS PICTURE. CURRENTLY SHOWS A DEFAULT USER.
return cell;
}
Thanks!
Found a simple solution: store the image of the selected contact in a new NSMutableArray at index 0 each time.
UIImage *image =[UIImage imageWithData:(__bridge NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail)];
[peopleImage insertObject:image atIndex:0];
The images can then be loaded like normal into a cell by calling the array in the UITabelView
cell.personImage.image = [peopleImage objectAtIndex:indexPath.row];
First, you get a UIImage from the ABRecordRef:
[UIImage imageWithData:(NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail)];
Then, you can set the UITableViewCell's imageView's image like:
[[cell imageView] setImage: image];
Couple other links that may help you:
http://www.chrisdanielson.com/tag/uitableviewcell/
http://goddess-gate.com/dc2/index.php/post/421
Related
Edit 1
To be clear, [self loadObjects] is not my method it is a method on the PFQueryTableViewController class supplied by parse to pull in new data.
I suspect this might be being caused by the table drawing code, as the tablecellview is configured to be auto-adjust it's height.
Here is the table drawing code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object
{
//Setup estimated heights and auto row height
self.tableView.estimatedRowHeight = 68.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
//Give the cell a static identifier
static NSString *cellIdentifier;
socialPost* post = object;
//Check to see what sort of cell we should be creating, text, image or video
if (object[#"hasImage"] != nil) {
cellIdentifier = #"posts_with_image";
} else {
cellIdentifier = #"posts_no_image";
}
//Create cell if needed
hashtagLiveCellView *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[hashtagLiveCellView alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
// Configure the cell to show our imformation, loading video and images if needed
cell.postTitle.text = [NSString stringWithFormat:#"#%#",object[#"userName"]];
cell.postText.text = [NSString stringWithFormat:#"%#",
object[#"text"]];
[cell.userImage setImageWithURL:[NSURL URLWithString:post.userImageURL]];
[cell.postImage setImageWithURL:[NSURL URLWithString:post.imageURL]];
//Set ID's on the custom buttons so we know what object to work with when a button is pressed
cell.approveButtonOutlet.stringID = object.objectId;
[cell.approveButtonOutlet addTarget:self action:#selector(approvePostCallback:) forControlEvents:UIControlEventTouchUpInside];
cell.deletButtonOutlet.stringID = object.objectId;
[cell.deletButtonOutlet addTarget:self action:#selector(deletePostCallback:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
Original
I have a PFQueryTableViewController that i am loading with object from parse.
I have a scheduled task set to run every 20 seconds that calls:
[self loadObjects]
To fetch any new objects, or any changed to objects that have happened.
That all works fine, however if i am scrolled halfway down the tableview when the loadObjects is called the page jumps back to the top. Even if there are no new or changed data available.
Is there an easy way around this, before i start looking into hacky ways to catch the reload and force the table to stay where it is.
Thanks
Gareth
When you're calling loadObjects you load the objects from start. And there for you get the first results again.
Try to change [self loadObjects]; to [self.tableView reloadData];.
I have table view set up in the traditional Master/Detail way in an iPhone app. When I click save on the detail screen (after adding the information I want), it switches back to the master list, however, the text that's supposed to appear in the cell in the Master list doesn't appear until I touch the cell with my finger, however, if I touch the cell with my finger it obviously segues to the detail screen and then I have to click "back" to get to the Master list where the cell now has the text that it's supposed to. Interestingly, the table view image is appearing immediately upon save - I don't have to touch the cell to make the image appear in the same way I have to make the text appear.
Clicking the "save" button in the detail screen runs this code. The mnemonicField.text gets saved to the currentTopic and I later set it to be the text that appears in the cell
- (IBAction)save:(id)sender {
[self.currentTopic setTopic:topicField.text];
[self.currentTopic setMnemonic:mnemonicField.text]; //this should appear in cell on master
[self.currentTopic setMood: self.mood];
[self.delegate AddTopicViewControllerDidSave];
}
The Master table view controller is the delegate referred to in the above method. Here is that method.
-(void)AddTopicViewControllerDidSave{
NSError *error = nil;
NSLog(#"saving topick");
NSManagedObjectContext *context = self.managedObjectContext;
if (![context save:&error]){
NSLog(#"Error: %#", error);
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
In cellForRowAtIndexPath, I call another method to setup the cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TopicCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];//sets up cell, see below
return cell;
}
Here's configureCell:atIndexPath which sets up the cell. Again, note that both the image and the textLabel.text are set in this method but that I have to touch the cell with my finger to actually make the text appear
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
// Configure the cell...
Topic *topic = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = topic.mnemonic;
NSLog(#"jokemood in configure cell %#", joke.mood);
UIImage *funnyimage = [UIImage imageNamed: #"nslaugh.png"];
UIImage *badimage = [UIImage imageNamed: #"nsbad.png"];
UIImage *crapimage = [UIImage imageNamed: #"nscrap.png"];
UIImage *mehimage = [UIImage imageNamed: #"nsmeh.png"];
UIImage *smileimage = [UIImage imageNamed: #"nssmile.png"];
switch ([topic.mood intValue]) {
case 0:
// do something
NSLog(#" topic table 0");
cell.imageView.image = crapimage ;
break;
case 1:
After some experimentation, I determined that the problem is this line cell.textLabel.text = topic.mnemonic; in the above function. If I take that line out, then the title shows in the cell in the master list immediately upon saving in the detail screen. However, if I take that line out, then when i start the application, the title is not getting assigned when the data's pulled from core data. So, either way there's a problem. If this line cell.textLabel.text = topic.mnemonic; is left in the above function, then I have to touch the cell in the master list (after saving in the detail) for the text to appear, but if I take that line out then the textLabel.text is not getting assigned when the application pulls from core data. Neither option is acceptable.
Do you know why this might be happening?
For the sake of completion here's prepareForSegue in the master view controller where I setup the currentTopic on the destination view controller based on the topic in the master view controller
if ([[segue identifier] isEqualToString:#"AddJoke"]){
MMAddTopicViewController *ajvc = (MMAddTopicViewController *)[segue destinationViewController];
ajvc.delegate = self;
ajvc.mood = nil;
Topic *newTopic = (Topic *)[NSEntityDescription insertNewObjectForEntityForName:#"Topic" inManagedObjectContext: [self managedObjectContext]];
NSMutableSet* relationship = [self.rootObject mutableSetValueForKey:#"subItems"];
NSManagedObject *lastObject = [self.fetchedResultsController.fetchedObjects lastObject];
double lastObjectDisplayOrder = [[lastObject valueForKey:#"displayOrder"] doubleValue];
[newTopic setValue:[NSNumber numberWithDouble:lastObjectDisplayOrder + 1.0] forKey:#"displayOrder"];
[relationship addObject:newJoke];
ajvc.currentTopic = newTopic; ///currentTopic in destination view controller is set to topic from master view controller
I added [self.tableView reloadData]; in the save method in the master view controller, which is called from the detail controller (master serves as the detail's delegate). The Lynda.com tutorial I copied it from didn't have to use self.tableView reloadData, not sure why my code did.
-(void)AddTopicViewControllerDidSave{
NSError *error = nil;
NSLog(#"saving cock");
NSManagedObjectContext *context = self.managedObjectContext;
if (![context save:&error]){
NSLog(#"Error: %#", error);
}
[self.navigationController popToRootViewControllerAnimated:YES];
[self.tableView reloadData];
}
I had an IPhone application in which i am loading images asynchronously from an array ,using this in cellforrowatindexpath,
` - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
sCell *cell = (sCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[sCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSMutableDictionary *dicttable=[sarray objectAtIndex:indexPath.section];
NSString *icon= [dicttable objectForKey:#"thumb_image"];
cell.image.image = [UIImage imageNamed:#"thumbnail-default.png"];
if(icon.length!=0)
{
if(![[ImageCache sharedImageCache] hasImageWithKey:icon])
{ cell.image.image = [UIImage imageNamed:#"thumbnail-default.png"];
NSArray *myArray = [NSArray arrayWithObjects:cell.image,icon,#"thumbnail-default.png",[NSNumber numberWithBool:NO],nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate performSelectorInBackground:#selector(updateImageViewInBackground:) withObject:myArray];
}
else
{
cell.image.image = [[ImageCache sharedImageCache] getImagefromCacheOrUrl:icon];
}
}`
and all working fine,Because i enabled paging i need to call the request to load the next set of values, like this
`- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section==[sarray count]-1)
{
[self performSelectorInBackground:#selector(loadingfeedcntents:) withObject:count];
}
}`
but here the problem is when i am scrolling the table view wrong images are assigned to the image views in the custom cell,Can anybody help me on this ?
This is most probably due the reuse of the cells. Probably what is happening is that you trigger an asynchronous request to download an image, while that is happening you scroll the table an that cell is reused so when the image finishes downloading that new cell gets the image instead of the one originally requested it.
I think you will have to find another way to do it or detach that request from the cell on prepareForReuse
Many people is recommending this class "SDWebImage" for that situation (although I haven't test it)
I have been using SDWebImage for last 2 years and have used in 5 apps, Really easy and I would say it is the best way to load images in background with caching.
Please give a try. You will love it.
A nice tutorial is here.
I have an app where I load a lot of large images. When I lazy-load them, and even after the image has been loaded, the cell does not load them until I take my finger off the screen. I am calling my downloadImageForVisiblePaths function in the UIScrollViewDelegate methods scrollViewDidEndDragging and in scrollViewDidEndDecelerating apart from this, I am also setting the image in the UITableView's cellForRowAtIndexPath method like so:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// Code to load reusable custom cell
CustomObject *object = (CustomObject*) [self.tableArray objectAtIndex: indexPath];
if(!object.mainImage){
[self downloadImageForIndexPath:indexPath];
cell.mainImageView.image = [UIImage imageNamed:#"placeholder"];
}else{
cell.mainImageView.image = object.mainImage;
}
return cell;
}
Where the downloadImageForIndexPath looks like this:
-(void) downloadImageForIndexPath:(NSIndexPath*) indexPath{
UIImage *loadedImage = [[UIImage alloc] init];
// take url and download image with dispatch_async
// Once the load is done, the following is done
CustomCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
cell.mainImageView.image = loadedImage;
CustomObject *object = (CustomObject*) [self.tableArray objectAtIndex: indexPath];
object.mainImage = loadedImage;
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableVIew reloadData];
});
}
I can't see where I am going wrong. I need the images to load even when the finger is on the screen. This behaviour is similar to how the images load on apps like Google+, Instagram or Facebook.
Any pointers will be much appreciated.
It's hard to tell since you didn't include all the code for downloadImageForIndexPath, but it looks like you are assigning an image to a cell from a background thread (you shouldn't touch UI controls from background threads). Also, if you'r updating cell directly, you don't need to call reloadData.
I would also suggest using SDWebImage for displaying remote images in a tableview.
I have a tableview in my app for messaging with custom tableviewcells. This tableview is populated with an array from JSON. I have a UIImageView in the cell that shows a blue dot image if the message is unread.
Here's some code:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
messagesArray = [self getMessages];
[messagesTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"MessagesCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
NSDictionary *messagesDictionary = [messagesArray objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
nameLabel.text = [messagesDictionary objectForKey:#"fromUserName"];
UIImageView *readImage = (UIImageView *)[cell viewWithTag:103];
NSNumber *boolNumber = [messagesDictionary valueForKey:#"readFlag"];
BOOL read = [boolNumber boolValue];
if (!read)
readImage.image = [UIImage imageNamed:#"Message Read Circle"];
return cell;
}
When the message is selected, I send a message to the server to let it know that the message is read, but when I go back, it still has the unread image. If I quit the app in the simulator and reload the app, the unread image is gone from the message that I selected, so I know the mark as read message is going through. Why won't [messagesTableView reloadData] work?
Since table view cells are reused, you should set the image in any case,
and not only if read == NO. Something like:
if (read)
readImage.image = [UIImage imageNamed:#"Message Read Circle"];
else
readImage.image = [UIImage imageNamed:#"Message Unread Circle"];
It doesn't look like you are actually calling reloadData on your tableview in your viewDidAppear like you said you are doing.
Also, like Mike Z asked above, you may be having issues with timing of your getMessages call. Is this method synchronous, or asynchronous? Posting some of that code may help as well.
Also, you need to make sure that you set your readImage to nil if your message has been read. Remember, these cells are dequeued, so if you don't set the imageView for both the true and false state of the read property, you may get erroneous results.