I've created an UITableView with cells and i am trying to add a person's phone and name information into a cell via some functions. The problem is when i try to add a person's info, it is being added into my data array but, i cannot see the new info on UITableView cell. I tried to use reloadData function to reload UITableView, it was no use.
I have arrays for people's numbers and names. Also, i have a button which adds a person via ABPeoplePickerNavigationController.
addedContacts.h :
#import <UIKit/UIKit.h>
#import AddressBook;
#import AddressBookUI;
#interface addedContacts : UIViewController <UITextFieldDelegate, ABPeoplePickerNavigationControllerDelegate, UITableViewDelegate,
UITableViewDataSource>{
NSMutableArray* people;
NSMutableArray* numbers;
IBOutlet UITableView *tableview;
}
- (IBAction)addPerson:(id)sender;
#end
I use commitEditingStyle function to delete a person's information from the tableview. In addition, I keep people's information in defaults of application.
addedContacts.m :
#import "addedContacts.h"
#implementation addedContacts
-(void)viewDidLoad{
[super viewDidLoad];
}
- (IBAction)addPerson:(id)sender {
ABPeoplePickerNavigationController* picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
[self dismissViewControllerAnimated:YES completion:nil];
}
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
people = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"people"]];
if(numbers.count == 0 && numbers.count == 0){
numbers = [[NSMutableArray alloc] init];
people = [[NSMutableArray alloc] init];
}
NSString *contactName = CFBridgingRelease(ABRecordCopyCompositeName(person));
NSString* nameField = [NSString stringWithFormat:#"%#", contactName ? contactName : #"No Name"];
ABMultiValueRef phoneRecord = ABRecordCopyValue(person, kABPersonPhoneProperty);
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phoneRecord, 0);
NSString* phoneField = (__bridge_transfer NSString *)phoneNumber;
CFRelease(phoneRecord);
[people addObject:nameField];
[numbers addObject:phoneField];
[defaults setObject:people forKey:#"people"];;
[defaults setObject:numbers forKey:#"numbers"];
[self dismissViewControllerAnimated:YES completion:nil];
return NO;
}
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
[people removeObjectAtIndex:indexPath.row];
[numbers removeObjectAtIndex:indexPath.row];
// Request table view to reload
[tableView reloadData];
}
-(NSInteger) numberOfSectionsInTableView: (UITableView*) tableView{
return 1;
}
-(NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger)section{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
return [numbers count];
}
-(UITableViewCell*) tableView: (UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
numbers = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"numbers"]];
people = [[NSMutableArray alloc] initWithArray:[defaults objectForKey:#"people"]];
NSLog(#"%#",numbers);
NSLog(#"%#",people);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
cell.accessoryType = UITableViewCellAccessoryDetailButton;
}
NSLog(#"%ld", (long)indexPath.row);
NSLog(#"%ld", (long)people.count);
NSString* Tname = [people objectAtIndex:indexPath.row];
NSString* Tnumber = [numbers objectAtIndex:indexPath.row];
cell.textLabel.text = Tname;
cell.detailTextLabel.text = Tnumber;
return cell;
}
#end
Any suggestions?
1) this is something strange:
if(numbers.count == 0 && numbers.count == 0){
numbers = [[NSMutableArray alloc] init];
people = [[NSMutableArray alloc] init];
}
i think you mean
if(numbers.count == 0 && people.count == 0)
2) i think you mast insert reload of table after you add your new object into people and numbers arrays
[people addObject:nameField];
[numbers addObject:phoneField];
[tableView reloadData];
My hunch is that, there this a bug in the tableView:numberOfRowsInSection code. You need to return count of people or numbers. Not get the value from the user defaults. Since they don't have the newly added person and number.
-(NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger)section{
return [numbers count];
}
Related
I have a UITableView which shows all of the songs in the Music Library, which works fine. However, when I start typing in the search bar to narrow down the search results, the app crashes immediately. Like as soon as I press one letter on the keyboard, it crashes. I've tried reworking my textDidChange: method but it always crashes, no matter what. I'm not sure what I'm doing wrong, could anyone help? Thanks.
Header:
#interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong,nonatomic)UITableView* tableView;
#property (strong,nonatomic)UISearchBar* searchBar;
#end
ViewController.m:
#import "PTTableViewController.h"
#implementation PTTableViewController
MPMediaQuery *songsQuery;
NSArray *songsArray;
NSMutableArray *filteredArray;
NSMutableArray *songTitlesArray;
-(void)viewDidLoad{
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:_tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
self.searchBar.delegate = self;
self.searchBar.placeholder = #"Search";
self.tableView.tableHeaderView = self.searchBar;
songsQuery = [MPMediaQuery songsQuery];
songsArray = [songsQuery items];
songTitlesArray = [[NSMutableArray alloc] init];
for (MPMediaItem *item in songsArray) {
[songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
}
filteredArray = [[NSMutableArray alloc] init];
filteredArray = [songTitlesArray copy];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return filteredArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];
return cell;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
NSLog(#"search changed");
[self performSelectorInBackground:#selector(helper) withObject:nil];
}
-(void)helper{
[filteredArray removeAllObjects];
if ([self.searchBar.text isEqualToString:#""]){
filteredArray = [songTitlesArray copy];
} else {
for (NSString *object in songTitlesArray){
if ([object rangeOfString:self.searchBar.text].location == NSNotFound){
NSLog(#"string not found");
} else {
NSLog(#"string found");
[filteredArray addObject:object];
}
}
} [self.tableView reloadData];
}
#end
First...
You're getting a crash because you do this:
filteredArray = [songTitlesArray copy];
Which assigns a non-mutable copy of songTitlesArray to filteredArray.
When you then try to do this:
[filteredArray removeAllObjects];
you're trying to mutate a non-mutable object.
You can fix this (first) problem in a couple ways...
One way is to change all instances of copy to mutableCopy:
filteredArray = [songTitlesArray mutableCopy];
However, using your exact code, you'll introduce new crashes because you'll be executing UI actions on a background thread.
Next... Are you sure you're getting "keyboard lag"?
You may want to change a few things:
make _filteredArray non-mutable
instead of calling removeAllObjects simply re-create the array
use filteredArrayUsingPredicate instead of looping through and comparing each string
Give this a try - because I don't have your MPMediaQuery I'll generate 500 strings as "song titles":
#interface PTTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong,nonatomic)UITableView* tableView;
#property (strong,nonatomic)UISearchBar* searchBar;
#end
#implementation PTTableViewController
//MPMediaQuery *songsQuery;
NSArray *songsArray;
NSArray *filteredArray;
NSMutableArray *songTitlesArray;
-(void)viewDidLoad{
[super viewDidLoad];
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0,[UIScreen mainScreen].bounds.size.width - 75,150) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:_tableView];
self.searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0,0,320,44)];
self.searchBar.delegate = self;
self.searchBar.placeholder = #"Search";
self.tableView.tableHeaderView = self.searchBar;
// songsQuery = [MPMediaQuery songsQuery];
// songsArray = [songsQuery items];
//
// songTitlesArray = [[NSMutableArray alloc] init];
// for (MPMediaItem *item in songsArray) {
// [songTitlesArray addObject:[item valueForProperty:MPMediaItemPropertyTitle]];
// }
// filteredArray = [[NSMutableArray alloc] init];
// filteredArray = [songTitlesArray copy];
// I don't have your Query, so let's create a 500 element string array,
// using the row number + one of the following words in each entry
NSString *samples = #"These are some random words for the song titles.";
// So, we'll have:
// "Row: 0 These"
// "Row: 1 are"
// "Row: 2 some"
// "Row: 3 random"
// "Row: 4 words"
// "Row: 5 for"
// "Row: 6 the"
// "Row: 7 song"
// "Row: 8 titles."
// "Row: 9 These"
// "Row: 10 are"
// "Row: 11 some"
// "Row: 12 random"
// "Row: 13 words"
// "Row: 14 for"
// ... etc
NSArray *a = [samples componentsSeparatedByString:#" "];
songTitlesArray = [[NSMutableArray alloc] init];
for (int i = 0; i < 500; i++) {
NSString *w = a[i % [a count]];
NSString *s = [NSString stringWithFormat:#"Row: %d %#", i, w];
[songTitlesArray addObject:s];
}
filteredArray = [songTitlesArray copy];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return filteredArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
cell.textLabel.text = [filteredArray objectAtIndex:indexPath.row];
return cell;
}
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
[self helper];
}
-(void)helper{
if ([self.searchBar.text isEqualToString:#""]){
filteredArray = [songTitlesArray copy];
} else {
NSString *w = [NSString stringWithFormat:#"SELF contains[c] '%#'", self.searchBar.text];
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:w];
filteredArray = [songTitlesArray filteredArrayUsingPredicate:sPredicate];
}
[self.tableView reloadData];
}
#end
As a side note, you've done a nice job of demonstrating one of the (many) reasons NOT to try and develop on a jailbroken device.
I have added my delegate method and
I have a UITableView with a list of names. It has sections with an alphabetical index on the right hand side (see picture).
My program crashes whenever I enter a first character in the search field. I get the following error:
UpdateSearchResultsForSearchController
[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
Understand I am trying to access an empty array, in the method UpdateSearchResultsForSearchController.
The program crashes in the last line of the method.
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
Here is the header
#import <UIKit/UIKit.h>
#import "EmployeeDatabase.h"
#interface EmployeeListViewController : UITableViewController<UISearchResultsUpdating, UISearchBarDelegate>
#property (nonatomic, strong) NSMutableArray *employees;
#property (nonatomic, strong) UISearchController *searchController;
#property (nonatomic, strong) NSMutableArray *tableSections;
#property (nonatomic, strong) NSMutableArray *tableSectionsAndItems;
#end
and here is the implementation
#import "EmployeeListViewController.h"
#import "EmployeeDetailViewController.h"
#implementation EmployeeListViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self initializeTableContent];
[self initializeSearchController];
[self styleTableView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Initialization methods
- (void)initializeTableContent {
self.employees = [EmployeeDatabase getEmployees];
self.tableSections = [NSMutableArray array];
self.tableSectionsAndItems = [NSMutableArray array];
for (employee *name in self.employees) {
NSString *key = [[name.lstNme substringToIndex: 1] uppercaseString];
if ([self.tableSections containsObject:key] == false) {
[self.tableSections addObject:key];
NSMutableArray *tmpArray = [NSMutableArray array];
[tmpArray addObject:name.fulNme];
NSMutableDictionary *tmpDictionary = [NSMutableDictionary dictionaryWithObject:tmpArray forKey:key];
[self.tableSectionsAndItems addObject:tmpDictionary];
} else {
NSMutableArray *tmpArray = [NSMutableArray array];
NSUInteger index = [self.tableSections indexOfObject:key];
NSMutableDictionary *tmpDictionary = [self.tableSectionsAndItems objectAtIndex:index];
tmpArray = [tmpDictionary objectForKey:key];
[tmpArray addObject:name.fulNme];
[self.tableSectionsAndItems removeObjectAtIndex:index];
[self.tableSectionsAndItems addObject:tmpDictionary];
}
}
}
- (void)initializeSearchController {
//instantiate a search results controller for presenting the search/filter results (will be presented on top of the parent table view)
UITableViewController *searchResultsController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
//instantiate a UISearchController - passing in the search results controller table
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
//this view controller can be covered by theUISearchController's view (i.e. search/filter table)
self.definesPresentationContext = YES;
//define the frame for the UISearchController's search bar and tint
self.searchController.searchBar.frame = CGRectMake(self.searchController.searchBar.frame.origin.x,
self.searchController.searchBar.frame.origin.y,
self.searchController.searchBar.frame.size.width, 44.0);
self.searchController.searchBar.tintColor = [UIColor whiteColor];
//add the UISearchController's search bar to the header of this table
self.tableView.tableHeaderView = self.searchController.searchBar;
//this ViewController will be responsible for implementing UISearchResultsDialog protocol method(s) - so handling what happens when user types into the search bar
self.searchController.searchResultsUpdater = self;
//this ViewController will be responsisble for implementing UISearchBarDelegate protocol methods(s)
self.searchController.searchBar.delegate = self;
}
- (void)styleTableView {
[[self tableView] setSectionIndexColor:[UIColor colorWithRed:100.0f/255.0f green:100.0f/255.0f blue:100.0f/255.0f alpha:1.0f]];
[[self tableView] setSectionIndexBackgroundColor:[UIColor colorWithRed:230.0f/255.0f green:230.0f/255.0f blue:230.0f/255.0f alpha:1.0f]];
}
#pragma mark - UITableViewDataSource methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.tableSections count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSDictionary *sectionItems = [self.tableSectionsAndItems objectAtIndex:section];
NSArray *namesForSection = [sectionItems objectForKey:[self.tableSections objectAtIndex:section]];
return [namesForSection count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [self.tableSections objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellReuseId = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellReuseId];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellReuseId];
}
NSDictionary *sectionItems = [self.tableSectionsAndItems objectAtIndex:indexPath.section];
NSArray *namesForSection = [sectionItems objectForKey:[self.tableSections objectAtIndex:indexPath.section]];
cell.textLabel.text = [namesForSection objectAtIndex:indexPath.row];
//show accessory disclosure indicators on cells only when user has typed into the search box
if(self.searchController.searchBar.text.length > 0) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
return cell;
}
#pragma mark - UITableViewDelegate methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *sectionItems = [self.tableSectionsAndItems objectAtIndex:indexPath.section];
NSArray *namesForSection = [sectionItems objectForKey:[self.tableSections objectAtIndex:indexPath.section]];
NSString *selectedItem = [namesForSection objectAtIndex:indexPath.row];
//Log
NSLog(#"User selected %#", selectedItem);
}
//#pragma Segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
employee *employee = self.employees[indexPath.row];
EmployeeDetailViewController *employeeDetailViewController = segue.destinationViewController;
employeeDetailViewController.detailItem = employee;
}
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
//only show section index titles if there is no text in the search bar
if(!(self.searchController.searchBar.text.length > 0)) {
NSArray *indexTitles = self.tableSections;
//HERE
//*indexTitles = [Item fetchDistinctItemGroupsInManagedObjectContext:self.managedObjectContext];
return indexTitles;
} else {
return nil;
}
}
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
view.tintColor = [UIColor colorWithRed:100.0f/255.0f green:100.0f/255.0f blue:100.0f/255.0f alpha:1.0f];
UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView *)view;
[header.textLabel setTextColor:[UIColor whiteColor]];
}
#pragma mark - UISearchResultsUpdating
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController {
//get search text from user input
NSString *searchText = [self.searchController.searchBar text];
//exit if there is no search text (i.e. user tapped on the search bar and did not enter text yet)
if(!([searchText length] > 0)) {
return;
}
//handle when there is search text entered by the user
else {
//based on the user's search, we will update the contents of the tableSections and tableSectionsAndItems properties
[self.tableSections removeAllObjects];
[self.tableSectionsAndItems removeAllObjects];
NSString *firstSearchCharacter = [searchText substringToIndex:1];
//handle when user taps into search bear and there is no text entered yet
if([searchText length] == 0) {
//self.tableSections = [[Item fetchDistinctItemGroupsInManagedObjectContext:self.managedObjectContext] mutableCopy];
//self.tableSectionsAndItems = [[Item fetchItemNamesByGroupInManagedObjectContext:self.managedObjectContext] mutableCopy];
}
//handle when user types in one or more characters in the search bar
else if(searchText.length > 0) {
//the table section will always be based off of the first letter of the group
NSString *upperCaseFirstSearchCharacter = [firstSearchCharacter uppercaseString];
self.tableSections = [[[NSArray alloc] initWithObjects:upperCaseFirstSearchCharacter, nil] mutableCopy];
//there will only be one section (based on the first letter of the search text) - but the property requires an array for cases when there are multiple sections
//NSDictionary *namesByGroup = [Item fetchItemNamesByGroupFilteredBySearchText:searchText ////inManagedObjectContext:self.managedObjectContext];
//self.tableSectionsAndItems = [[[NSArray alloc] initWithObjects:namesByGroup, nil] mutableCopy];
}
//now that the tableSections and tableSectionsAndItems properties are updated, reload the UISearchController's tableview
[((UITableViewController *)self.searchController.searchResultsController).tableView reloadData];
}
}
#pragma mark - UISearchBarDelegate methods
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
[self.tableSections removeAllObjects];
[self.tableSectionsAndItems removeAllObjects];
//self.tableSections = [[Item fetchDistinctItemGroupsInManagedObjectContext:self.managedObjectContext] mutableCopy];
//self.tableSectionsAndItems = [[Item fetchItemNamesByGroupInManagedObjectContext:self.managedObjectContext] mutableCopy];
}
#end
The problem is that, your are removing all objects at this line
[self.tableSectionsAndItems removeAllObjects];
and you have commented the lines, which again feels that array, just above the lines which you mentioned in your question. so, uncomment the following lines
//NSDictionary *namesByGroup = [Item fetchItemNamesByGroupFilteredBySearchText:searchText ////inManagedObjectContext:self.managedObjectContext];
//self.tableSectionsAndItems = [[[NSArray alloc] initWithObjects:namesByGroup, nil] mutableCopy];
And in numberOfRows Method, you are accessing the object at index on empty array that leads to crash.
[self.tableSectionsAndItems objectAtIndex:section];
So, uncomment those two lines above, in the following method and it will fix.
-(void)updateSearchResultsForSearchController:(UISearchController *)searchController
Try and share your results.
I want to store address book of iOS in array and display that in UITableView. This is the code I am using. I just want contact name and phone number to be displayed.
pragma mark- address book methods
(BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
ABPeoplePickerNavigationController *picker= [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
return NO;
}
(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person{
ABMultiValueRef phone = (ABMultiValueRef) ABRecordCopyValue(person, kABPersonPhoneProperty);
ABMultiValueRef personID = (ABMultiValueRef) ABRecordCopyValue(person, kABPersonFirstNameProperty);
CFStringRef phoneID = ABMultiValueCopyValueAtIndex(phone, 0);
//CFStringRef personName= ABMultiValueCopyValueAtIndex(personID, 0);
NSLog(#"%#",[NSString stringWithFormat:#"%#", phoneID]);
NSLog(#"%#",[NSString stringWithFormat:#"%#", personID]);
}
Please refer to Rays addressbook tutorial. I think it should answer all your questions.
Why are u calling the picker navigator on beginning of a text? You should call it through a button added as a right bar button item. Then select the persons you want and store them in a mutable array. After that dismiss the controller and load the table view.
Edited:
Please find the code below for your reference. I hope it might help in understanding how it is working. Here is the reference link to the sample code as well.
Interface:
#import <UIKit/UIKit.h>
#interface JSViewController : UITableViewController
#end
Implementation:
#import "JSViewController.h"
#import <AddressBookUI/AddressBookUI.h>
#import <AddressBook/AddressBook.h>
#interface JSViewController ()<ABPeoplePickerNavigationControllerDelegate,UITableViewDataSource,UITableViewDelegate>
#property (nonatomic, strong) NSMutableArray *peopleSelected;
#end
#implementation JSViewController
-(NSMutableArray *)peopleSelected
{
if (!_peopleSelected) {
_peopleSelected = [NSMutableArray new];
}
return _peopleSelected;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIBarButtonItem *selectPerson = [[UIBarButtonItem alloc] initWithTitle:#"Select" style:UIBarButtonItemStyleBordered target:self action:#selector(btnSelectHandler:)];
self.navigationItem.rightBarButtonItem = selectPerson;
}
- (void)btnSelectHandler:(id)sender
{
ABPeoplePickerNavigationController *peoplePicker = [ABPeoplePickerNavigationController new];
peoplePicker.peoplePickerDelegate = self;
[self.navigationController presentViewController:peoplePicker animated:YES completion:^{
}];
}
#pragma mark - ABPeoplePickerNavigationControllerDelegate
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;
{
[self dismissViewControllerAnimated:YES completion:^{
if (self.peopleSelected) {
[self.tableView reloadData];
}
}];
}
The below method is deprecated in iOS 8.0. You can use the function
- peoplePickerNavigationController:didSelectPerson:
instead of this:
//Deprecated in iOS 8.0.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
NSString *name = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
ABMultiValueRef phoneNumbers = (ABRecordCopyValue(person, kABPersonPhoneProperty));
NSString *mobile = #"";
NSString *label = #"";
for (CFIndex i=0 ; i<ABMultiValueGetCount(phoneNumbers); i++) {
label = (__bridge NSString *)ABMultiValueCopyLabelAtIndex(phoneNumbers, i);
if ([label isEqualToString:(NSString *)kABPersonPhoneMobileLabel]) {
mobile = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phoneNumbers, i);
}else if([label isEqualToString:(NSString*)kABPersonPhoneIPhoneLabel]){
mobile = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phoneNumbers, i);
break;
}
}
NSDictionary *dict = #{#"name":name,
#"phone":mobile};
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self.phone == %# && self.name == %#",mobile,name];
NSArray *personAlready = [self.peopleSelected filteredArrayUsingPredicate:predicate];
NSLog(#"personAlready %#",personAlready);
if (!personAlready.count) {
[self.peopleSelected addObject:dict];
}
return NO;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
return YES;
}
#pragma mark - UITableViewDataSource
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.peopleSelected.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
NSDictionary *dict = self.peopleSelected[indexPath.row];
cell.textLabel.text = dict[#"name"];
return cell;
}
#pragma mark - UITableViewDelegate
#end
I have a ChatViewController where I send and receive messages and store the messages as a NSMutableDictionary in an NSArray.
NSMutableArray *messages; //in header file
- (void)addMessage:(NSString *)receivedMessage :(NSString *) sender
{
[self reloadDataInTableView:receivedMessage :sender];
}
- (void)reloadDataInTableView:(NSString *)message :(NSString*)sender
{
NSLog(#"Text: %# Sender: %#", message, sender);
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:message forKey:#"msg"];
[m setObject:sender forKey:#"sender"];
[messages addObject:m];
[self.tView reloadData];
NSLog(#"Number of rows: %d", [messages count]);
}
When I am calling 'addMessage' from AppDelegate, both the strings are passed but it cannot add it to the 'messages' because the 'Number of rows' always is zero. But when I am storing it from that class itself then the messages are getting stored and row count increases.
As a result, it only shows the messages from the ChatViewController but not the messages sent from the AppDelegate. I hope I was able to explain the problem properly.
Here is the cellForRowAtIndexPath function:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableDictionary *s = (NSMutableDictionary *) [messages objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [s objectForKey:#"msg"];
cell.detailTextLabel.text = [s objectForKey:#"sender"];
cell.accessoryType = UITableViewCellAccessoryNone;
cell.userInteractionEnabled = NO;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [messages count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Yes, I have initialized the messages array in viewDidLoad. But the problem is when I am trying to insert into the messages array from the AppDelegate the count is not increasing. In fact, it always shows zero. But when I insert from the ViewController itself, it maintains the count. Here is the AppDelegate code:
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{
if ([message isChatMessageWithBody])
{
XMPPUserCoreDataStorageObject *user = [xmppRosterStorage userForJID:[message from]
xmppStream:xmppStream
managedObjectContext:[self managedObjectContext_roster]];
NSString *messageBody = [[message elementForName:#"body"] stringValue];
NSString *displayName = [user jidStr];
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
ChatViewController *cvc = [[ChatViewController alloc] init];
[cvc reloadDataInTableView:messageBody :displayName];
}
else
{
// We are not active, so use a local notification instead
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.alertAction = #"Ok";
localNotification.alertBody = [NSString stringWithFormat:#"From: %#\n\n%#",displayName,messageBody];
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
}
}
}
When you do
ChatViewController *cvc = [[ChatViewController alloc] init];
[cvc reloadDataInTableView:messageBody :displayName];
in your app delegate, you are instantiating a new ChatViewController. Even if the messages array was initialised in the init method (which it isn't, as you said it is initialised in the viewDidLoad method), this new instance of the ChatViewController is discarded at the end of your xmppStream:didReceiveMessage: method and is definitely not the same that is showing on screen.
You need to keep a reference to the proper instance of ChatViewController in your app delegate and use that instead of instantiating a new one...
If you want some more specific advice on how to approach this problem, you'll need to give us more detail about your whole project's architecture (using storyboard? is there a navigation controller? tab controller? how does the chatviewcontroller relate to other view controllers? etc. etc.).
You need
messages = [[NSMutableArray alloc] init];
in viewdidload
Try this:
- (void)reloadDataInTableView:(NSString *)message :(NSString*)sender
{
NSLog(#"Text: %# Sender: %#", message, sender);
NSMutableDictionary *m = [[NSMutableDictionary alloc] init];
[m setObject:message forKey:#"msg"];
[m setObject:sender forKey:#"sender"];
if (!messages)
{
messages = [[NSMutableArray alloc] init];
}
[messages addObject:m];
[self.tView reloadData];
NSLog(#"Number of rows: %d", [messages count]);
}
I want developed a similar alarm application to remind user send message, and my question is I want get user field in data like numbers, message and date to table view by array.
I do this use tabbar controller,my first view is gave to user field in all data and check save button. My second view is table view to remind user.
Now I can save the data in NSMutableArray by one time only, where I save the data again, the old data with replace. Anyone know how to do this?
My code here.
Here is save button for another view.
I call FirstView
- (IBAction)SaveAll:(id)sender{
if(numText.text == nil || textView.text == nil || Selectime == nil){
NSLog(#"error becos value null");
UIAlertView *errorview = [[UIAlertView alloc]initWithTitle:#"Error Info" message:#"You nomber , message text or date no input or select.Pls fill in all. "delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorview show];
[errorview release];
}else{
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (localNotif == nil)
return;
DataClass *obj=[DataClass getInstance];
localNotif.fireDate = obj.showdate;
NSString *getdate = [obj.showdate description];
[defaults setObject:getdate forKey:#"date"];
NSLog(#"localNotif = %#", localNotif.fireDate);
localNotif.timeZone = [NSTimeZone localTimeZone];
obj.showdate = nil;
NSString *getnum =numText.text;
[defaults setObject:getnum forKey:#"keyToLookupString"];
// Notification details
// localNotif.alertBody = [eventText text];
NSString *getmessage = self.textView.text;
[defaults setObject:getmessage forKey:#"message"];
localNotif.alertBody = self.textView.text;
NSLog(#"alertBody message = %#",localNotif.alertBody);
// Set the action button
localNotif.alertAction = #"Send Now?";
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.applicationIconBadgeNumber = 1;
// Specify custom data for the notification
NSDictionary *infoDict = [NSDictionary dictionaryWithObject:#"someValue" forKey:#"someKey"];
localNotif.userInfo = infoDict;
// Schedule the notification
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
[localNotif release];
numText.text = #"";
textView.text = #"";
Selectime.text = #"";
self.textView.placeholder = NSLocalizedString(#"Event Here....",);
SaveDataView *savedata = [[SaveDataView alloc]initWithNibName:#"SaveView" bundle:nil];
[savedata.tableview reloadData];
}
}
In my table view controller I call SaveDataView.
SaveDataView.h
#import <UIKit/UIKit.h>
#interface SaveDataView : UIViewController <UITableViewDataSource,UITableViewDelegate>{
IBOutlet UITableView *tableview;
NSMutableArray *MessageArray;
NSMutableArray *NomberArray;
NSMutableArray *DateArray;
}
#property (nonatomic, retain) IBOutlet UITableView *tableview;
#property (nonatomic,retain)NSMutableArray *MessageArray;
#property (nonatomic,retain)NSMutableArray *NomberArray;
#property (nonatomic,retain)NSMutableArray *DateArray;
#end
SaveDataView.m
#import "SaveDataView.h"
#import "DataClass.h"
#import "SMSAppDelegate.h"
#implementation SaveDataView
#synthesize tableview;
#synthesize MessageArray,NomberArray,DateArray;
- (void) viewWillAppear {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *messageShow =[defaults stringForKey:#"message"];
NSString *nomberShow = [defaults stringForKey:#"keyToLookupString"];
NSString *dateShow = [defaults stringForKey:#"date"];
[MessageArray addObject:messageShow];
NSLog(#"MessageArray = %#",MessageArray);
[NomberArray addObject:nomberShow];
NSLog(#"NomberArray = %#",NomberArray);
[DateArray addObject:dateShow];
NSLog(#"DateArray = %#",DateArray);
[self.tableview reloadData];
NSLog(#"checking.... ");
//[super viewWillAppear:animated];
}
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"table view viewDidLoad");
MessageArray = [[NSMutableArray alloc] init];
NomberArray = [[NSMutableArray alloc] init];
DateArray = [[NSMutableArray alloc] init];
// [MessageArray insertObject:messageShow atIndex:[MessageArray count]];
[self viewWillAppear];
[super viewDidLoad];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#pragma mark -
#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 [[[UIApplication sharedApplication] scheduledLocalNotifications] count];
NSLog(#"in nsinteger tableview");
return [self.MessageArray count];
return [self.NomberArray count];
return [self.DateArray count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"in uitableviewcell...");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
// if (cell == nil) {
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
//}
// Configure the cell...
//NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
//UILocalNotification *notif = [notificationArray objectAtIndex:indexPath.row];
// NSString *enNote = [[NSString alloc] initWithString:string];
NSString *showmessage = [MessageArray objectAtIndex:indexPath.row];
NSString *shownomber = [NomberArray objectAtIndex:indexPath.row];
NSString *showdate = [DateArray objectAtIndex:indexPath.row];
[cell.textLabel setText:showmessage];
NSLog(#"table view settext = %#",showmessage);
cell.textLabel.textColor = [UIColor blackColor];
NSString *abc = [NSString stringWithFormat:#"%# \n %#", shownomber,showdate];
//NSString *abc = [NSString stringWithFormat:#"%# \n %#", nomber,[notif.fireDate description]];
[cell.detailTextLabel setText:abc];
cell.detailTextLabel.numberOfLines = 2;
NSLog(#"table view detailTextLabel = %#,%#",shownomber,showdate);
cell.detailTextLabel.textColor = [UIColor redColor];
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
return cell;
[self.tableview reloadData];
}
#end
Replace the viewDidLoad with viewWillappear...viewDidLoad is called only once when you loaded the view so you cant able to replace the array. viewWillappear is called everytime when the view is appeared on the screen...
EDIT:afer seeing the comments.
keep the viewdidload as it is . add these codes to your application.
-(void)viewwillappear
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *messageShow =[defaults stringForKey:#"message"];
NSString *nomberShow = [defaults stringForKey:#"keyToLookupString"];
NSString *dateShow = [defaults stringForKey:#"date"];
[MessageArray addObject:messageShow];
NSLog(#"MessageArray = %#",MessageArray);
[NomberArray addObject:nomberShow];
NSLog(#"NomberArray = %#",NomberArray);
[DateArray addObject:dateShow];
NSLog(#"DateArray = %#",DateArray);
}