NSMutableArray can't add object from JSONModelArray? - ios

I have a NSMutableArray and adding objects to it as below:
// Do any additional setup after loading the view.
array = [[NSMutableArray alloc] init];
[array addObject:#"Apple"];
[array addObject:#"Orange"];
[array addObject:#"Mango"];
[array addObject:#"Banana"];
But when I try to add object from JSONModelArray still showing the above 4 object only.
Here is JSONModelArray code:
//fetch the feed
_collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:#"http://localhost:8888/json/collectionBusinessFinal.json"
completion:^(JSONModel *model, JSONModelError *err) {
for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){
NSLog(#"%#",item);
NSString *title = [item valueForKey:#"name"];
[array addObject:title];
NSLog(#"%#",array);
// here array is printing all the objects with the above 4 objects
}
}];
But return array here below only showing above 4 objects not the all the objects added from JSONModelArray.
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [array count];
// only showing 4 above obects like Apple, Banana...
}
Here is my full implementation file:
#import "CollectionViewController.h"
#import "AccordionViewController.h"
#import "JSONModelLib.h"
#import "CollectionBusinessManager.h"
#import "HUD.h"
#interface CollectionViewController (){
CollectionBusinessManager* _collectionBusinessFeeds;
}
#end
#implementation CollectionViewController
NSMutableArray* array;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
array = [[NSMutableArray alloc] init];
[array addObject:#"Apple"];
[array addObject:#"Orange"];
[array addObject:#"Mango"];
[array addObject:#"Banana"];
//show loader view
[HUD showUIBlockingIndicatorWithText:#"Fetching JSON"];
//fetch the feed
_collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:#"http://localhost:8888/json/collectionBusinessFinal.json"
completion:^(JSONModel *model, JSONModelError *err) {
//hide the loader view
[HUD hideUIBlockingIndicator];
[self.v reloadData];
NSLog(#"%#", _collectionBusinessFeeds.collectionBusinesses);
for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){
NSLog(#"%#",item);
NSString *title = [item valueForKey:#"name"];
[array addObject:[title copy]];
NSLog(#"%#",array);
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark collection view methods
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [array count];
}
- (UIColor*) randomColor{
int r = arc4random() % 255;
int g = arc4random() % 255;
int b = arc4random() % 255;
return [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UILabel *label = (UILabel *) [cell viewWithTag:100];
label.text = [array objectAtIndex:indexPath.row];
cell.backgroundColor = [self randomColor];
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath{
AccordionViewController *detailVC = [[AccordionViewController alloc] init];
detailVC.passParam= [array objectAtIndex:indexPath.row];
detailVC.title = detailVC.passParam;
[self.navigationController pushViewController:detailVC animated:YES];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
can anybody help me?
Update:
at last it solved my problem adding the property like below:
#property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
[self.collectionView reloadData];

For what you are describing, it seems like the fetch is being called after the collection view has calculated the number of items. You have to reload the collection view after fetching your new results.

UPDATED
#import "CollectionViewController.h"
#import "AccordionViewController.h"
#import "JSONModelLib.h"
#import "CollectionBusinessManager.h"
#import "HUD.h"
#interface CollectionViewController (){
CollectionBusinessManager* _collectionBusinessFeeds;
}
#property (nonatommic, strong) NSMutableArray *array;
#end
#implementation CollectionViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.array = [[NSMutableArray alloc] init];
[self.array addObject:#"Apple"];
[self.array addObject:#"Orange"];
[self.array addObject:#"Mango"];
[self.array addObject:#"Banana"];
But when I try to add object from JSONModelArray still showing the above 4 object only.
Here is JSONModelArray code:
//fetch the feed
__weak typeof(self) weakSelf = self;
_collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:#"http://localhost:8888/json/collectionBusinessFinal.json"
completion:^(JSONModel *model, JSONModelError *err) {
for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){
NSLog(#"%#",item);
NSString *title = [item valueForKey:#"name"];
[weakSelf.array addObject:title];
NSLog(#"%#", weakSelf.array);
// here array is printing all the objects with the above 4 objects
}
}];
Old Answer
If you add __block to your array initialisation it must works.
// Do any additional setup after loading the view.
__block NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"Apple"];
[array addObject:#"Orange"];
[array addObject:#"Mango"];
[array addObject:#"Banana"];
But when I try to add object from JSONModelArray still showing the above 4 object only.
Here is JSONModelArray code:
//fetch the feed
_collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:#"http://localhost:8888/json/collectionBusinessFinal.json"
completion:^(JSONModel *model, JSONModelError *err) {
for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){
NSLog(#"%#",item);
NSString *title = [item valueForKey:#"name"];
[array addObject:title];
NSLog(#"%#",array);
// here array is printing all the objects with the above 4 objects
}
}];

//fetch the feed
_collectionBusinessFeeds = [[CollectionBusinessManager alloc] initFromURLWithString:#"http://localhost:8888/json/collectionBusinessFinal.json"
completion:^(JSONModel *model, JSONModelError *err) {
for(NSDictionary *item in _collectionBusinessFeeds.collectionBusinesses){
NSLog(#"%#",item);
NSString *title = [item valueForKey:#"name"];
[array addObject:title];
[collectionView reloadData];
NSLog(#"%#",array);
}
}];
I have edited code, please check above code.

Related

Why does my app crash when UISearchBar's textDidChange is called?

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.

UITableView with Section, IndexList and Search

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.

Adding data via person picker but, UITableView do not reload

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];
}

retaining data outside of viewDidLoad in objective c ios

I am aware this question has been asked previously, but the answers provided have not solved my issue.
For instance, I have a very simple array of objects outlined in viewDidLoad:
#implementation MyViewController {
NSArray *tableData;
}
- (void)viewDidLoad
{
[super viewDidLoad];
tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is"];
NSLog(#"My Data: %#", tableData);
which is called in a tableView using cellForRowAtIndexPath
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
This works fine and the NSLog shows my array. However, when i outline tableData outside of viewDidLoad, my array is (null).
My question is, how do I make my array available for the tableView when it is specified outside of ViewDidLoad?
edit: Here is my specific code:
#import <UIKit/UIKit.h>
#import "PhotoView.h"
#interface FrontViewController : UIViewController
#property (nonatomic, retain) UITableView *tableView;
#end
#import "FrontViewController.h"
#import "StreamScreen.h"
#import "API.h"
#import "PhotoView.h"
#import "StreamPhotoScreen.h"
#import "PrivateViewController.h"
#import "SWRevealViewController.h"
#import "PhotoScreen.h"
#import "RearViewController.h"
#import "SimpleTableCell.h"
#interface FrontViewController()
// Private Methods:
- (IBAction)pushExample:(id)sender;
#end
#implementation FrontViewController{
NSArray *tableData;
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = NSLocalizedString(#"Front View", nil);
SWRevealViewController *revealController = [self revealViewController];
[self.navigationController.navigationBar addGestureRecognizer:revealController.panGestureRecognizer];
UIBarButtonItem *revealButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"reveal-icon.png"]
style:UIBarButtonItemStyleBordered target:revealController action:#selector(revealToggle:)];
self.navigationItem.leftBarButtonItem = revealButtonItem;
// This works if I uncomment
//tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is", nil];
[self refreshStream];
}
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:
[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil]
onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [[NSMutableArray alloc] init];
myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
[self loadData];
tableData = userNameData;
[self.tableView reloadData];
// I can see my json array in NSLog
NSLog(#"here's the results: %#", tableData);
}];
}
//This doesn't work either
//-(void)loadData {
// Add the data to your array.
//tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is", nil];
//NSLog(#"My Data: %#", tableData);
// Now load the table view.
// [self.tableView reloadData];
//}
-(void)showStream:(NSArray*)stream {
for (int i=0;i<[stream count];i++) {
NSDictionary* photo = [stream objectAtIndex:i];
}
NSArray *checkData = [stream valueForKey:#"username"];
//I can see my data in NSLog
NSLog(#"here's the results: %#", checkData);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"didSelectRowAtIndexPath");
/*UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:#"You've selected a row" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];*/
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:[tableData objectAtIndex:indexPath.row] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
// Display the Hello World Message
[messageAlert show];
// Checked the selected row
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"willSelectRowAtIndexPath");
if (indexPath.row == 0) {
return nil;
}
return indexPath;
}
#end
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:
[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil]
onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
}];
tableData = userNameData;
[self.tableView reloadData];
}
You're falling into a very common trap with asynchronous programming here.
commandWithParams takes a completion block, which is where you are getting the data out of the JSON. This block is not executed until the API call has returned. The sequence of events that happens when you run this code is:
commandWithParams is called
tableData is assigned to the contents of userNameData (which presumably you've also declared somewhere else otherwise this would not even compile)
reloadData is called
.... time passes
The completion block is executed and the JSON is read out into local variables, which are then instantly destroyed.
You need to move the two lines (points 2 and 3 in the list above) inside the completion block. There will be no data for your table until the block returns.
Ok my understanding of your question is that you want to assign variables to your NSArray in another method (not viewDidLoad) and then load the table view.
This is simple, just make a method which is in charge of adding the data to your array and then reload your table view like so:
-(void)viewDidLoad {
[super viewDidLoad];
// Call your method.
[self loadData];
}
-(void)loadData {
// Add the data to your array.
tableData = [NSArray arrayWithObjects:#"Hello", #"My", #"Name", #"Is"];
NSLog(#"My Data: %#", tableData);
// Now load the table view.
[myTableView reloadData];
}
Update 1
It would be much more helpful if you could share your code with us. How your and setting up your tableview, when its being called/etc....
Update 2
Ok well it seems obvious what yoru issue is. Your table view will never load like that. You need to call the tableview reloadData method outside the cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
return cell;
}
So the method call [self.tableView reloadData]; should only be called in your refreshStream method.
Update 3
You need to initialize your NSMutableArray before you can add data to it. initialize it in your refreshStrem method like so:
-(void)refreshStream {
// call the "stream" command from the web API
[[API sharedInstance] commandWithParams:[NSMutableDictionary dictionaryWithObjectsAndKeys:#"stream", #"command", nil] onCompletion:^(NSDictionary *json) {
//got stream
[self showStream:[json objectForKey:#"result"]];
NSMutableArray *myData = [[NSMutableArray alloc] init];
myData = [json objectForKey:#"result"];
NSArray *userNameData = [myData valueForKey:#"username"];
}];
tableData = userNameData;
[self.tableView reloadData];
}
Update 4
Ok well after reading #jrturton answer, I think its safe to assume that my answer is rubbish. To anyone reading my answer, please view #jrturton post.
Well I feel pretty sheepish. The answer was simple. I was so hung up on Json and API that all I didn't check the basics. All I need was in my .h file:
#property (strong, nonatomic) IBOutlet UITableView *tableView;
I had originally had:
#property (nonatomic, retain) UITableView *tableView;

EXC_BAD_ACCESS crash when switching back and forth between views

I'm getting a EXC_BAD_ACCESS crash when switching back and forth between views. I'm having a problem finding the cause of this crash. In the simulator it always goes back to the main.m file and reports the crash in it.
But on my device the EXC_BAD_ACCESS show up on my custom UITableViewCell when I release it in the dealloc method. If I enable NSZombieEnabled my app doesn't crash at all.
Here is the .h file
#import <UIKit/UIKit.h>
#define kWinsAmountTagValue 2 // how many wins you have
#define kWinningsAmountTagValue 3 // how much money you won
#interface MyStatsViewController : UIViewController
<UITableViewDelegate, UITableViewDataSource,
UINavigationBarDelegate, UINavigationControllerDelegate>{
NSArray *list;
UITableView *theTable;
UITableViewCell *theCell;
}
#property (nonatomic, retain) NSArray *list;
#property (nonatomic, retain) IBOutlet UITableView *theTable;
#property (nonatomic, retain) IBOutlet UITableViewCell *theCell;
// dealloc and cleanup
-(void) dealloc;
// misc methods
-(void)loadData;
// demo data
-(NSArray *)tableData;
#end
Here is my .m file
#import "MyStatsViewController.h"
#implementation MyStatsViewController
#synthesize list;
#synthesize theTable;
#synthesize theCell;
#pragma mark - dealloc and cleanup
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
NSLog(#"Memory Warning");
// 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;
self.list = nil;
self.theTable = nil;
self.theCell = nil;
}
- (void)dealloc
{
[super dealloc];
[list release];
[theTable release];
[theCell release];
}
#pragma mark - misc methods
-(void) loadData
{
self.list = [self tableData];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(void)viewWillAppear:(BOOL)animated
{
[self loadData];
[theTable reloadData];
}
#pragma mark - Table Data Source Methods
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return [list count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier =#"MyStatsCustomCellIdentifer";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
NSUInteger row = [indexPath row];
if (cell == nil) {
if (row == [list count] -1) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
} else {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyStatsCustomCell"
owner:self
options:nil];
if ([nib count] > 0) {
cell = self.theCell;
} else {
NSLog(#"failed to load MyStatsCustomCell");
}
}
}
// Add custom stuff here for rows
//cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (row == [list count] -1) {
cell.textLabel.text = [list objectAtIndex:row];
} else {
UILabel *prizeLevel = (UILabel *)[cell viewWithTag:kPrizeLevelTagValue];
prizeLevel.text = [[list objectAtIndex:row] objectForKey:#"prizeLevel"];
UILabel *winsAmount = (UILabel *)[cell viewWithTag:kWinsAmountTagValue];
winsAmount.text = [[list objectAtIndex:row] objectForKey:#"winsAmount"];
UILabel *winningsAmount = (UILabel *)[cell viewWithTag:kWinningsAmountTagValue];
winningsAmount.text = [[list objectAtIndex:row] objectForKey:#"winningsAmount"];
}
//NSLog(#"theCell Retain: %i",[theCell retainCount]);
return cell;
}
#pragma mark - Table View Delegate Methods
-(void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
#pragma mark - demo data
-(NSArray *)tableData
{
NSArray *prizeLevels = [[NSArray alloc] initWithObjects:
#"6-of-6", #"5-of-6", #"4-of-6",#"3-of-6", nil];
NSArray *winsAmount = [[NSArray alloc] initWithObjects:
#"0", #"0", #"2", #"100", nil];
NSArray *winngingsAmount = [[NSArray alloc] initWithObjects:
#"$0",#"$0", #"$45.50",#"$125.00", nil];
NSMutableArray *myGames = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [prizeLevels count]; i++) {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:[prizeLevels objectAtIndex:i] forKey:#"prizeLevel"];
[dict setObject:[winsAmount objectAtIndex:i] forKey:#"winsAmount"];
[dict setObject:[winngingsAmount objectAtIndex:i] forKey:#"winningsAmount"];
[myGames addObject:dict];
[dict release];
}
[prizeLevels release];
[winsAmount release];
[winngingsAmount release];
[myGames addObject:#"Spent: $1250.00"];
return myGames;
}
#end
Any help would be appreciated.
It is a good practice to clean up class's own variables before calling the super's destructor. A lot more details can be found here: Why do I have to call super -dealloc last, and not first?.

Resources