Today I encountered bug, that I'm unable to replicate and it is very confusing for me.
Ok little background:
I'm currently working on app, that has tab bar controller as initial view controller. There are several nav controllers connected to different tab bar items.
One of them is a tableViewController, that is populated from JSON.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
//NSMutableArray for storing loaded values
[pics addObject:imageLoad];
[names addObject:[aucdict objectForKey:#"name"]];
[idcka addObject:[aucdict objectForKey:#"auction_id"]];
// Configure the cell...
cell.nameLabel.text = [aucdict objectForKey:#"name"];
cell.priceLabel.text = [NSString stringWithFormat:#"%#",priceString];
cell.timeLabel.text = [NSString stringWithFormat:#"%#",timeString];
cell.thumbnailImageView.image = imageLoad;
return cell;}
After clicking on row, I perform performSegueWithIdentifier:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self performSegueWithIdentifier:#"showAuctionDetail" sender:self];}
and in prepareForSegue I send some data to next ViewController
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
namesArray = [[NSArray alloc] initWithArray:names];
picsArray =[[NSArray alloc] initWithArray:pics];
IDarray = [[NSArray alloc] initWithArray:idcka];
if ([segue.identifier isEqualToString:#"showAuctionDetail"])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
detailViewController *dViewController = segue.destinationViewController;
dViewController.selectedAuctionTitle = [namesArray objectAtIndex:indexPath.row];
dViewController.auctionPic = [picsArray objectAtIndex:indexPath.row];
dViewController.id_aukcie = [IDarray objectAtIndex:indexPath.row];
}}
Now comes my problem. Sometimes (this really confuses me, because it I haven't found when does it happen) when I start the application, and tap on some row, I get totally different data passed to DetailViewController. The only thing, I can guess is that my arrays are different (they contain more or less values) than actual JSON response. But that would mean, my app would crash if I clicked on first or last item in table (index out of bounds or something like that), that never happened.
I've seen this bug happened maybe 5 times randomly. I tried to run and quit app for 20 times in a row and it happened only once.
P.S. I know that class name (detailViewController) should start with capital letter, I apologize for that :)
edited: as rdelmar suggested
I think the problem is having the call out to the server in the cellForRowAtIndexPath: method. You should put that code in viewDidLoad, and then when the data has come back, and is finished parsing, call reloadData on your table view.
Related
Hi I think I have encountered a somewhat unique bug with my code. I believe it has to do with my iPhone storing some type of cached interface.
Here are some pictures highlighting the bug.
This first picture shows how I want my table cells to be displayed as and
how they are displayed in the IOS Simulator
and here is how the table is displayed on my phone
as you can see through the images my phone has failed to display the same table view as the simulator.
The Xib file I am working with matches the simulator view.
Originally I had the xib designed like how it is on the phone but changed it a couple days ago and haven't gotten the view to be able to update correctly on the phone.
here is the code I am using to generate the table.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"TableViewCell";
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:simpleTableIdentifier owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSLog(#"Here are the book names, %#",_bookNameArray);
NSLog(#"here are the prices, %#", _priceOfBookArray);
cell.priceOfBookLabel.text = [_priceOfBookArray objectAtIndex:indexPath.row];
cell.bookNameLabel.text = [_bookNameArray objectAtIndex:indexPath.row];
cell.authorNameLabel.text = [_authorNameArray objectAtIndex:indexPath.row];
cell.bookImageLabel.file = _bookImageData[indexPath.row];
[cell.bookImageLabel loadInBackground];
return cell;
}
Does anyone have any ideas as to what might be causing the problem?
Any help greatly appreciated.
I have an app with some posts placed at an UITableView. Each post have a favorite button and I need to change its image when the user clicks on it. Here are the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = #"identifier";
CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if(cell == nil){
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"myNib" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSDictionary *post = [posts objectAtIndex:indexPath.row];
[cell.likeButton addTarget:self
action:#selector(clickedOnLike:)
forControlEvents:UIControlEventTouchUpInside];
cell.likeButton.tag = indexPath.row;
And on click handler:
-(IBAction)clickedOnLike:(id)sender
{
int tag = buttonSender.tag;
NSDictionary *post = [posts objectAtIndex:tag];
if( ![self likedAlready:post] ){
//set liked on this view...
//update view
NSLog(#"button: %#",buttonSender);
[sender setImage:[UIImage imageNamed:#"newImage.png"] forState:UIControlStateNormal];
//send like to server...
}
}
At this point, everything is going alright. The problem is, after click a button, update the view and scroll to other cells, the other buttons views I never clicked are updated too. For example, when I click a button at indexPath 1, the ones at 5 and 9 change their images automatically. This is a mistery to me, since I call the action sender directly and update only it. Thanks for help.
The reusable cell do it: dequeueReusableCellWithIdentifier.
U need to deal manually with things like this.
U can invoke manually to your clickedOnLike: method from the CellForRowAtIndexPath:
Try to maintain inside your modal (from your MVC development architecture), and access there from the CellForRowAtIndexPath:
That will solve your problem :)
Good Luck!
In your cellForRowAtIndexPath, set the default image of your button, then change it if its a favorite. The rows tend to get cached so always set your data to what you expect it to be.
As others have said, the problem is because the view is recycled for everyline, and in the recycled copy the button may have had its picture changed to the new one.
you need to keep somewhere in memory the status of the post, and on the cellForRowAtIndexpath set the correct image for the button every time the row is rendered.
Alternatively, if you have a very small number of posts, just eliminate recycling by doing
static NSString *identifier = nil;
After getting the data from multiple device, I am reloading tableview but tableview is flashing the labels. For example, two rows are in tableview contains two labels each. When I call reload tableview, displaying data in first row and second row will be empty and when displaying second row, first row will be empty. Like that it is flashing please help me how can I solve this
like this I am reloading tableview
[devicesTableView performSelectorOnMainThread:#selector(reloadData)
withObject:nil
waitUntilDone:NO];
This is CellForAtIndexPath Method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"DeviceCustomTableViewCellIdentifier";
devicesCustomTableViewCell = (DeviceCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (devicesCustomTableViewCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DeviceCustomTableViewCell" owner:self options:nil];
devicesCustomTableViewCell = [nib objectAtIndex:0];
}
DeviceDetails *deviceDetailsEntity = [devicesArray objectAtIndex:indexPath.row];
devicesCustomTableViewCell.deviceName.text = deviceDetailsEntity.deviceLocalName;
for (int i=0; i<[dataArray count]; i++) {
DeviceParticulars *deviceParticulars = [dataArray objectAtIndex:i];
if ([[[deviceParticulars.peripheral identifier] UUIDString] isEqualToString:deviceDetailsEntity.deviceAddress]) {
devicesCustomTableViewCell.temperatureValueLabel.text = deviceParticulars.tempReadOutStr;
}
In this, DeviceDetails class is core data class, In that I am saving BLE device name as per requirement.
DeviceParticulars class is NSObject class for saving data from multiple BLE devices like I am getting temperature from multiple devices. I am displaying Temp values in tableview.
dataArray is an array contains DeviceParticulars object.
Reloading the entire table every time a peripheral value changes is expensive and, as you are seeing, has visual impacts.
You can change your custom cell to act as a delegate to your model object - DeviceParticulars
In your DeviceParticulars.h file, register the delegate property and protocol
#property (weak,nonatomic) id delegate;
#protocol DeviceParticularsDelegate
- (void)didUpdateDevice:(DeviceParticulars *)device;
#end
In your DeviceParticulars.m file, where you update readings, call
[self.delegate didUpdateDevice:self];
Then in your DeviceCustomTableViewCell.h, add <DeviceParticularsDelegate> to the class definition and add a property to store your deviceParticulars
#property (strong,nonatomic) DeviceParticulars *myDevice;
In the .m implement the delegate method
-(void)didUpdateDevice:(DeviceParticulars *)device {
// Update cell labels as required
}
and implement prepareForReuse
- (void)prepareForReuse {
self.myDevice.delegate=nil; // Remove this cell as delegate for existing device;
}
Finally, set the delegate in your cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"DeviceCustomTableViewCellIdentifier";
devicesCustomTableViewCell = (DeviceCustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (devicesCustomTableViewCell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DeviceCustomTableViewCell" owner:self options:nil];
devicesCustomTableViewCell = [nib objectAtIndex:0];
}
DeviceDetails *deviceDetailsEntity = [devicesArray objectAtIndex:indexPath.row];
devicesCustomTableViewCell.deviceName.text = deviceDetailsEntity.deviceLocalName;
for (int i=0; i<[dataArray count]; i++) {
DeviceParticulars *deviceParticulars = [dataArray objectAtIndex:i];
if ([[[deviceParticulars.peripheral identifier] UUIDString] isEqualToString:deviceDetailsEntity.deviceAddress]) {
deviceParticulars.delegate=deviceCustomTableViewCell; //Set the delegate
devicesCustomTableViewCell.myDevice=deviceDetails; //Set device property
devicesCustomTableViewCell.temperatureValueLabel.text = deviceParticulars.tempReadOutStr;
}
Try using the -dequeueReusableCellWithIdentifier:forIndexPath: method for iOS 6 and later. It will automatically instantiate a cell if there is no reusable one, so you don't have to check if cell==nil. Not sure what causes your problems but I think it's worth to try it.
Please let me know if it helps.
I am currently making a tableView for my app, and I've got my cells from a .plist file. However I want to be able to enter my second view controller no matter, what cell I click on, the difference should be that the UIText for my second viewController will depend on which cell I click.
So far, I have imagined something like;
if(tableview.tag == 1){
myUIText.text = #"a";
}else if(tableview.tag == 2){
myUIText.text = #"b";
etc.......
Will this work using the same view controller, same UIText? If not, then how?
And how can I set the tags for my array in my .plist file?
I would appreciate your answers
EDIT: Here is a sample of two methods;
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.items count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString * id = #"plistdata";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:id forIndexPath:indexPath];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:id];
}
cell.textLabel.text = self.items[indexPath.row];
return cell;
}
More information is certainly needed by I will make some assumptions on your code and see if it helps any. I assume the the text you want to send is the title of the cell the person clicked on. Now how you send that to the next VC will differ depending on if you are using a segue or not. If you are using a segue you can do something like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"mySegue"]) {
UITableViewCell *cell = (UITableViewCell *)sender;
UpcomingViewController *viewController = segue.destinationViewController;
viewController.textView.text = cell.chapterText;
}
}
However if you are using didSelectRowAtIndexPath then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UpcomingViewController *viewController = [[UpcomingViewController alloc] init];
viewController.textView.text = self.arrayOfChapterText[indexPath.row];
[self.navigationController pushViewController:viewController animated:YES];
}
Now again because your question wasn't very clear on your scenarios I have taken some liberties with what I think you are probably trying to do. I assume that the view controller that will show after you choose an item will have the UITextView as a public property so that you can set it before pushing or segueing to your new view. I also assume in the didSelectRowAtIndexPath that you are using a UINavigationController.
Hope this helps.
I am creating a simple app to select video urls out of a UITable. I have hooked my data source and delegate to a UIViewController subclass and the table is filled correctly. Also, it recognizes selections and prints to the log.
My issue is that it gets a "EXC_BAD_ACCESS" and crashes when I select a cell. I am looking through the code and the error propagates to this method:
-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString* cellIdentifier = #"SelectionIdentifier";
//Errors start happening this next line
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
//NSString* str = [[videos objectAtIndex:[indexPath row]] lastPathComponent];
NSString* test = #"test";
[[cell textLabel] setText:test];
return cell;
}
-(void) tableView:(UITableView*)myTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// NSLog(#"Selected!");
}
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
return [videos count];
}
I'm not sure why this error is getting thrown in this method. Any fixes? I double checked to make sure the videos array wasn't nill.
I did another app that used this same method and it doesn't cause these errors.
Any help or suggestions would be greatly appreciated!
Instead of testing if(cell == nil) try using if(!cell). Honestly I'm not sure this is the issue, but after reviewing this I do not think that the error is not actually inside this method (it may somehow be related which is why it brings you here).
If this is only after you select a cell though, why is this method being called?
I think you should also call this method.Because this is preliminary delegate method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
After seeing your tableView i am not finding any problem at all.Try that may be it will be solve.