I'm attempting to allow my user to be able to delete/remove a row from a tableView (remove an object from an existing NSMutableArray), however when I try and delete the row, I get the following crash error:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: '-[__NSCFArray
removeObjectAtIndex:]: mutating method sent to immutable object'
Does anyone know why this might be? See code below. Apologies for the lengthy code.
.h
#property (strong, nonatomic) NSMutableArray *descripData;
.m
- (void)viewDidLoad {
[super viewDidLoad];
self.descripData = [[NSMutableArray alloc] init];
refreshControl = [[UIRefreshControl alloc]init];
[self.tableView addSubview:refreshControl];
[refreshControl addTarget:self action:#selector(refreshTable) forControlEvents:UIControlEventValueChanged];
NSMutableDictionary *viewParams = [NSMutableDictionary new];
[viewParams setValue:#"storeditems" forKey:#"view_name"];
[DIOSView viewGet:viewParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.descripData = responseObject;
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([self.storageData count] > 0 && self.descripData.count > 0)
{
return [self.descripData count];
}
else
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *DoctorsTableIdentifier = #"StorageItemTableViewCell";
StorageItemTableViewCell *cell = (StorageItemTableViewCell *)[tableView dequeueReusableCellWithIdentifier:DoctorsTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"StorageItemTableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
if (self.storageData.count > 0 && self.descripData.count > 0) {
noitemsView.hidden = YES;
NSDictionary *title = [self.descripData objectAtIndex:indexPath.row];
[[cell itemName] setText:[title objectForKey:#"node_title"]];
NSDictionary *node = [self.descripData objectAtIndex:indexPath.row];
[[cell itemDescrip] setText:[node objectForKey:#"body"]];
NSDictionary *value = [self.descripData objectAtIndex:indexPath.row];
[[cell valueLabel] setText:[value objectForKey:#"dollarvalue"]];
NSLog(#"%#", self.descripData);
NSDictionary *quantity = [self.descripData objectAtIndex:indexPath.row];
[[cell quantityLabel] setText:[quantity objectForKey:#"quantity"]];
NSLog(#"%#", self.descripData);
NSString *secondLink = [[self.descripData objectAtIndex:indexPath.row] objectForKey:#"photo"];
[cell.itemPhoto sd_setImageWithURL:[NSURL URLWithString:secondLink]];
}
else {
noitemsView.hidden = NO;
}
return cell;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 152;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.descripData removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView reloadData];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ItemDetailViewController *detailViewController = [[ItemDetailViewController alloc] initWithNibName:#"ItemDetailViewController" bundle:nil];
detailViewController.itemDetail = [[values objectAtIndex:indexPath.row] objectForKey:#"node_title"];
detailViewController.itemDetail = [self.descripData objectAtIndex:indexPath.row];
detailViewController.secondLink = self.descripData[indexPath.row][#"photo"];
[self.navigationController pushViewController:detailViewController animated:YES];
}
Check the line self.descripData = responseObject;. It looks like you are probably setting your descripData property to point to a NSArray, not an NSMutableArray. If you really need the mutability, try self.descripData = [responseObject mutableCopy]; instead.
I suspect that the following assignment causes the problem:
self.descripData = responseObject;
The response object itself is immutable, assigning it to a NSMutableArray does not change that fact, you need to create a NSMutableArray with the contents of the responseObject:
self.descripData = [NSMutableArray arrayWithArray:responseObject];
Most likely happening on the following line:
self.descripData = responseObject;
You're replacing your previous mutable array for descripData with a new one, which apparently is not mutable. You can either create a new mutable array from this new array using [NSMutableArray arrayWithArray:responseObject], or you can add the contents of responseObject to your current mutable array.
It's important to pay attention when you assign something of type id to a variable, because there's no compile-time type checking that can occur. This sometimes results in errors at runtime.
Face this issue because NSUserDefaults always returns an immutable object.
Please while retrieving Mutable object from NSUserDefaults please refer below code.
NSMutableArray *storeArray = [[defaultDefects objectForKey:#"defaultStoreDefects"]mutableCopy];
Related
I'm sorting my array by last name alphabetically. I'd like to separate this into sections with the appropriate header above each section (A, B, C, etc.).
Here's what I've tried below:
// Here is where I refresh the data and sort it based on last name
- (void)refreshData {
[[PCMSSessionManager sharedSession] refreshPCMSDataWithCompletion:^(BOOL success, NSString *errorMessage, id resultObject) {
if (success) {
NSLog(#"yay!");
self.membersArray = [[PCMSSessionManager sharedSession] memberArr];
// Let's sort the array
self.sortedArray = [self.membersArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(PCMSMember*)a lastName];
NSString *second = [(PCMSMember*)b lastName];
return [first compare:second];
}];
[self.tableView reloadData];
} else {
NSLog(#"boooo!!!!");
}
}];
}
- (NSDictionary *)indexedMembers
{
NSMutableDictionary *indexedContacts = [NSMutableDictionary new];
for (PCMSMember *member in self.sortedArray)
{
NSString *sortString = member.lastName;
NSString *sortLetter = [sortString substringToIndex:1];
/* see if that letter already exists as an index */
BOOL foundKey = NO;
for (NSString *key in [indexedContacts allKeys])
{
if ([key isEqualToString:sortLetter])
{
foundKey = YES;
}
}
NSMutableArray *valueArray;
if (foundKey)
{
valueArray = [((NSArray *)indexedContacts[sortLetter]) mutableCopy];
}
else
{
valueArray = [NSMutableArray new];
}
[valueArray addObject:member];
indexedContacts[sortLetter] = [valueArray copy];
}
return [indexedContacts copy];
}
// Here's my table data
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[[self indexedMembers] allKeys] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *indexedContacts = [self indexedMembers];
NSArray *myKeys = [indexedContacts allKeys];
NSString *key = myKeys[section];
return [((NSArray *)[self indexedMembers][key]) count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Configure the cell...
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
if (self.isPhysician == YES) {
NSString *key = [[self indexedMembers] allKeys][indexPath.section];
PCMSMember *currentMember = ((NSArray *)[self indexedMembers][key])[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", currentMember.firstName, currentMember.lastName];
}
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[self indexedMembers] allKeys][section];
}
UPDATE:
This is getting me closer to what I want.
The data is loading, it's being grouped properly and the headers are showing.
But it's not in alphabetical order.
How can I improve this code to show alphabetically?
It's showing in alphabetical order in my console, just not in the app.
The NSMutableDictionary is unordered by definition. It is not the natural choice if you rely on the order of the stored objects. I suggest you to use NSMutableArray instead. To store the tableview data for each section you can use this mini class
#interface MembersWithSameInitial : NSObject
#property (strong) NSString* initial;
#property (strong) NSMutableArray<PCMSMember*>* members;
#end
#implementation MembersWithSameInitial
#end
After you have sorted the members, all the data for the tableview can be produced with this before tableView reload.
NSMutableArray<MembersWithSameInitial*>* groupedMembers = [[NSMutableArray alloc] init];
for (PCMSMember* member in sortedArray) {
NSString* inicial = [member.lastName substringToIndex:1];
MembersWithSameInitial* last = [groupedMembers lastObject];
if (last && [last.initial isEqualToString:inicial]) {
[last.members addObject:member];
} else {
MembersWithSameInitial* newGroup = [[MembersWithSameInitial alloc] init];
newGroup.initial = inicial;
newGroup.members = [[NSMutableArray alloc] initWithObjects:member, nil];
[groupedMembers addObject:newGroup];
}
}
Since the structure of groupedMembers fits to a grouped tableView, the dataSource methods will have trivial implementations. Assuming, that you have stored groupedMembers in a property.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.groupedMembers.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.groupedMembers[section].members.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
PCMSMember *currentMember = self.groupedMembers[indexPath.section].members[indexPath.row];
//...
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return groupedMembers[section].initial;
}
Suggestion:
Create two properties
#property NSMutableArray *keys; // for the letters in alphabetical order
#property NSMutableDictionary *indexedContacts; // same as your implementation.
In the method refreshData call the method to create the data source and then reload the table view on the main thread.
Actually you don't need the properties memberArray and sortedArray anymore. The sorted array is passed to the method to create the data source.
- (void)refreshData {
[[PCMSSessionManager sharedSession] refreshPCMSDataWithCompletion:^(BOOL success, NSString *errorMessage, id resultObject) {
if (success) {
NSLog(#"yay!");
self.membersArray = [[PCMSSessionManager sharedSession] memberArr];
// Let's sort the array
NSArray *sortedArray = [self.membersArray sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSString *first = [(PCMSMember*)a lastName];
NSString *second = [(PCMSMember*)b lastName];
return [first compare:second];
}];
[self indexMembers:sortedArray];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
} else {
NSLog(#"boooo!!!!");
}
}];
}
The method indexMembers initializes the properties keys and indexedContacts and creates the data source.
- (void)indexMembers:(NSArray *)sortedMembers
{
self.keys = [[NSMutableArray alloc] init];
self.indexedContacts = [[NSMutableDictionary alloc] init];
for (PCMSMember *member in sortedMembers)
{
NSString *sortString = member.lastName;
NSString *sortLetter = [sortString substringToIndex:1];
/* see if that letter already exists as an index */
NSArray *keyArray = self.indexedContacts[sortLetter];
NSMutableArray *valueArray;
if (keyArray) {
// array for key exists, use it
valueArray = [keyArray mutableCopy];
} else {
// array for key does not exist, create a new one
valueArray = [NSMutableArray new];
// and add the letter to keys
[self.keys addObject:sortLetter];
}
[valueArray addObject:member];
self.indexedContacts[sortLetter] = [valueArray copy];
}
}
numberOfSectionsInTableView returns the number of keys
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.keys.count;
}
numberOfRowsInSection gets the appropriate array for the given section and returns the number of items.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSString *letter = self.keys[section];
NSArray *memberArray = self.indexedContacts[letter];
return memberArray.count;
}
In cellForRowAtIndexPath use the method dequeueReusableCellWithIdentifier: forIndexPath: to get always a valid cell. Then like in numberOfRowsInSection get the actual member array and populate the label.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if (self.isPhysician == YES) {
NSString *letter = self.keys[indexPath.section];
NSArray *memberArray = self.indexedContacts[letter];
PCMSMember *currentMember = memberArray[indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", currentMember.firstName, currentMember.lastName];
}
return cell;
}
titleForHeaderInSection simply returns the letter for the section
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return self.keys[section];
}
You're calling indexedMembers too much. This is very expensive.
I couldn't test the code, maybe there is a self or something else missing but you get an impression of the workflow.
Hi I am trying to display detail information of json data in second table view but their is some mistake in my logic so please help me my json response is in image and I am trying to to dispaly channel response in table view when user click the same cat_id then display all related channel list and image in second tableViewcell please help me..
-(void)getCategories
{
Service *srv=[[Service alloc]init];
NSString *str=#"http://streamtvbox.com/site/api/matrix/";//?country_code=91&phone=9173140993&fingerprint=2222222222";
NSString *method=#"channels";
NSMutableDictionary *dict=[[NSMutableDictionary alloc]init];
[srv postToURL:str withMethod:method andParams:dict completion:^(BOOL success, NSDictionary *responseObj)
{
NSDictionary *dict = [[NSDictionary alloc]initWithDictionary:[responseObj valueForKey:#"categories"]];
arrayChannelList=[responseObj valueForKey:#"channels"];
NSLog(#"Array : %#",channels);
for (NSDictionary *dict in arrayChannelList)
{
//NSArray *tempTitle = [[NSArray alloc]init];
arrayCat_Id = [dict objectForKey:#"cat_id"];
}
tempArray = [[NSArray alloc]init];
tempArray = [dict allKeys];
for(int i=0; i<tempArray.count; i++)
{
[arrayCategories addObject:[dict valueForKey:[tempArray objectAtIndex:i]]];
}
NSLog(#"%#",arrayCategories);
[tblView reloadData];
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"%lu",(unsigned long)[arrayCategories count]);
return arrayCategories.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ChannelCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [arrayCategories objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailChannelsViewController *detailVC = (DetailChannelsViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"DetailView"];
detailVC.channelTitle = [arrayCategories objectAtIndex:indexPath.row];
detailVC.SubChannelName = arrayCat_Id;
NSLog(#"%#",arrayCat_Id);
[self.navigationController pushViewController:detailVC animated:YES];
}
Just initialize of your NSmutuableArray and add the value of with addobject like:-
arrayCat_Id=[[NSMutableArray alloc]init];
for(your logic){
[arrayCat_Id addobject:[dict objectForKey:#"cat_id"];
}
Prompt, here is a code that sorts the dictionary by distance, I need to pass a variable coord to detailview where it is separated into position and put the marker on karte.No why coord gives 0 and how do I transfer it to the coordinates?
- (void)viewDidLoad
{[self setupArray];
[super viewDidLoad];}
-(void)setupArray{ self.myLocationManager = [[CLLocationManager alloc] init];
[states setObject: #12 forKey:#"60.050043,30.345783"];
[states setObject: #11 forKey:#"60.037389,30.322094"];
[states setObject: #32 forKey:#"60.037329,30.322014"];
[states setObject: #1 forKey:#"59.957387,30.324681"];
NSLog(#"%f",betweenDistance);
NSArray* sortedStates = [states keysSortedByValueUsingComparator: ^(id obj1, id obj2)
{
if ([obj1 floatValue] > [obj2 floatValue])
{
return (NSComparisonResult)NSOrderedDescending;
}
if ([obj1 floatValue] < [obj2 floatValue])
{
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];
NSLog(#"%#", sortedStates);
NSMutableArray* rows = [[NSMutableArray alloc] init];
for (NSString* key in sortedStates)
{
CGFloat distance = [[states objectForKey:key] floatValue];
[rows addObject:[NSString stringWithFormat:#"Distance is %f km", distance]];
}
datasource=rows;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 4;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Detail2ViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
[self.navigationController pushViewController:detail animated:YES];
NSString* coord = sortedStates[indexPath.row];
NSLog(#"virable coord %#",coord);
}
UPDATE
2015-04-23 13:25:07.361 gfhfgh[503:7361] 8845784.000000
2015-04-23 13:25:07.363 gfhfgh[503:7361] (
"59.957387,30.324681",
"60.037389,30.322094",
"60.050043,30.345783",
"60.037329,30.322014"
)
2015-04-23 13:25:07.620 gfhfgh[503:7361] Google Maps SDK for iOS version: 1.9.14591.0
2015-04-23 13:25:10.294 gfhfgh[503:7361] as (null) it is virable coord
2015-04-23 13:25:10.296 gfhfgh[503:7361] my viraible (null)
2015-04-23 13:25:10.296 gfhfgh[503:7361] ((null)) was false: provideAPIKey: should be called at most once
UPDATE2
I use this function, it may be because it does not work?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
//---------- CELL BACKGROUND IMAGE -----------------------------
UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.frame];
UIImage *image = [UIImage imageNamed:#"LightGrey.png"];
imageView.image = image;
cell.backgroundView = imageView;
[[cell textLabel] setBackgroundColor:[UIColor clearColor]];
[[cell detailTextLabel] setBackgroundColor:[UIColor clearColor]];
cell.textLabel.text = [datasource objectAtIndex:indexPath.row];
//Arrow
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
It's not a good practice to call [self setupArray] before [super viewDidLoad]. Call it before.
Try to call UITableView's method - (void)reloadData after loading your data on - (void)setupArray to tell tableView's delegate that you have loaded or updated your data and want to show it on the tableView. It should resolve your problem.
FYI: In your case, numberOfRowsInSection: should return [sortedStates count] to be sure that when you tap a cell, your app will not crash.
Update
- (void)viewDidLoad
{
[super viewDidLoad];
//Call setup array after [super viewDidLoad]
[self setupArray];
}
-(void)setupArray {
//Do all your setupArray stuff
//Now update the table contents
[yourTableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//Use [sortedStates count] here to be sure your app will not crash later
return [sortedStates count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Detail2ViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
[self.navigationController pushViewController:detail animated:YES];
NSString* coord = sortedStates[indexPath.row];
NSLog(#"virable coord %#",coord);
}
Update 2
I think I found the reason ! You have a instance variable called sortedStates, but inside - (void)setupArray you are declaring a local variable with the same name, and thats why you are not getting the values within the other methods.
Try changing
NSArray* sortedStates = [states keysSortedByValueUsingComparator: ^(id obj1, id obj2) {
//stuff
}];
with
//without "NSArray *"
sortedStates = [states keysSortedByValueUsingComparator: ^(id obj1, id obj2) {
//stuff
}];
Its because sortedStates[indexPath.row]; will result into a NSDictionary.
So code should be :
NSDictionary *coord = sortedStates[indexPath.row];
Pass this value to your detailViewController and extract the coordinate there.
I am recieving this error when i scroll to the bottom of my TableView, I dont think its any error with actually retrieving the pictures from the server.:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '-[__NSCFArray objectAtIndex:]: index (15) beyond bounds (15)'
Here is my .m file I cut it to only the actually needed parts of the file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
NSString *created = [tweet objectForKey:#"created_at"];
NSLog(#"%#", created);
static NSString *CellIdentifier = #"TweetCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *text = [tweet objectForKey:#"text"];
NSString *name = [[tweet objectForKey:#"user"] objectForKey:#"name"];
cell.textLabel.text = text;
cell.detailTextLabel.text = [NSString stringWithFormat:#"by %#", name];
return cell;
}else {
static NSString *CellIdentifier = #"InstagramCell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *entry = [self entries][indexPath.row];
NSString *imageUrlString = entry[#"images"][#"low_resolution"][#"url"];
NSURL *url = [NSURL URLWithString:imageUrlString];
[cell.imageView setImageWithURL:url];
return cell;
}
}
- (void)fetchTweets {
self.twitterClient = [[AFOAuth1Client alloc] initWithBaseURL:[NSURL URLWithString:#"https://api.twitter.com/1.1/"] key:#"TWEETER_KEY" secret:#"TWEETER_SECRET"];
[self.twitterClient authorizeUsingOAuthWithRequestTokenPath:#"/oauth/request_token" userAuthorizationPath:#"/oauth/authorize" callbackURL:[NSURL URLWithString:#"floadt://success"] accessTokenPath:#"/oauth/access_token" accessMethod:#"POST" scope:nil success:^(AFOAuth1Token *accessToken, id responseObject) {
[self.twitterClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self.twitterClient getPath:#"statuses/home_timeline.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSArray *responseArray = (NSArray *)responseObject;
[responseArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(#"Success: %#", obj);
tweets = responseArray;
[self.tableView reloadData];
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
} failure:^(NSError *error) {
NSLog(#"Error: %#", error);
}];
}
There needs to be tight coordination between the return value from numberOfRowsInSection and the array access that the code does in cellForRowAtIndexPath.
Consider this, your entries array and tweets array each have 4 elements. So numberOfRowsInSection returns 8. The cellForRowAtIndexPath method gets called to configure row 6. Your code will do this: NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
But wait... that array has only 4 elements, right? Asking for something at index 6 will generate the crash you see.
It might be simpler to write a methods to interleave the arrays into a single array, then answer the count of the combined array in numberOfRowsInSection. In cellForRowAtIndexPath, the array elements themselves should be able to tell you what kind of row you have (not the index). Dereference the combined array and configure the table accordingly.
EDIT - I'll try to make my advice more explicit in code: Let's say, for simplicity, that "entries" and "tweets" are both arrays of NSDictionaries and that your app wants to organize them in the UI entries first, then tweets.
// in interface:
#property (nonatomic, strong) NSArray *myModel;
// in code:
- (NSArray *)myModel {
if (!_myModel) {
NSMutableArray *array = [NSMutableArray arrayWithArray:[self entries]];
[array addObjectsFromArray:tweets];
_myModel = [NSArray arrayWithArray:array];
}
return _myModel;
}
We call this 'myModel' for a reason. It's the datasource of the table. The datasource protocol is asking explicitly about this array (and no other).
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.myModel.count;
}
Now cellForRowAtIndexPath is going to ask you to configure that many (myModel count) rows, numbered 0..count-1. You must dereference the same array -- myModel -- for all datasource methods:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *myModelForThisRow = self.myModel[indexPath.row];
// get the cell = deque...
cell.textLabel.text = myModelForThisRow[#"someKey"];
return cell;
}
What if your tweets or entries array changes? No problem, just rebuild the model like this:
- (IBAction)tweetsOrEntriesDidChange:(id)sender {
self.myModel = nil; // the "lazy" getter will rebuild it
[self.tableView reloadData]; // this will call the datasource which will call the lazy getter
}
You are trying to go read into an array outside of it's bounds.
That array access look very suspicious
if (indexPath.row % 2 == 0) {
NSDictionary *tweet = [tweets objectAtIndex:indexPath.row];
as well as this one
NSDictionary *entry = [self entries][indexPath.row];
From what I've seen your array tweets and [self entries] don't contain as many object each as there is row in your table section.
I take my assomption from here :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self entries] count] + tweets.count;
}
NSRangeException is thrown because you are trying to access an index which is not within the valid range for your array. Try setting an "Exception breakpoint" in Xcode to see where it's coming from. Check here to know more about Exception breakpoints
This is typically caused by an off by one error.
My NSMutableArray seems to be releasing and I can't figure out where, here is my code:
- (void)awakeFromNib {
arrayOfData = [[NSMutableArray alloc] initWithCapacity:0];
[arrayOfData addObject:#"test"];
NSLog(#"array: %#", arrayOfData);
}
All is well here!
Then I create a UITable and add it to the view, then I request some data, everything is normal.
arrayOfData = [response objectForKey:#"results"];
NSLog(#"count: %i", [arrayOfData count]);
NSLog(#"success!");
numberOfRows = [arrayOfData count];
[self.myTableView reloadData];
Creating the number of rows works fine too:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"test: %i", numberOfRows);
return numberOfRows;
}
Now accessing my array gets a "message sent to deallocated instance" at that line.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// crashes here!
NSLog(#"array: %i", [arrayOfData count]);
if (numberOfRows){
NSString *filename = [[arrayOfData objectAtIndex:indexPath.row] objectForKey:#"filename"];
NSString *url = [NSString stringWithFormat: #"http://www.web.com/dir/%#", filename];
[cell.imageView setImageWithURL:[NSURL URLWithString:url]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
}
return cell;
}
I have also have this in my header, and I used #synthesize as well
#property (nonatomic, retain) NSMutableArray *arrayOfData;
Any help would be appreciated!
It looks like you're reassigning arrayOfData with this line:
arrayOfData = [response objectForKey:#"results"];
If that's what you want, try this instead:
arrayOfData = [[response objectForKey:#"results"] retain];