I am currently trying to set up my tableview, when its first loaded I call my connection class which in turn calls my parser class then inside my parser class I call a method in my ViewController, which is the original view that is being set up. This method is passed an array which will be used latter.
The method passes the array to an array variable in this ViewController, in this method I then call
[self.tableView reloadData];
and what I am wanting that to do is reload cellForRowAtIndexPath so that it will go through my logic (if statements) and check if ([returnedArray count] != 0){ then do its thing.. but the thread never makes it back to this delegate method, which in turn never makes it back to the if statment.
MORE INFO :)
So first of all, when the the ViewController loads
tableView:cellForRowAtIndexPath: is called and sets up my UITableView that all looks perfect, it then calls my NSURLConnection method which connects to my server downloads all of the data and then passes that over to my parser class. From there my parser dose its thing, and everything is fine.
This is what the code looks like in my tableView:cellForRowAtIndexPath:, method
//..
if (indexPath.row == 0){
if ([FilterArray count] == 0){
[cellActivityIndicator startAnimating];
//-- Start NSURLConnection
EngineRequests *engineRequests = [[EngineRequests alloc] init];
[engineRequests initalizePacketVariables];
}
if ([FilterArray count] != 0){
[cellActivityIndicator stopAnimating];
cell.accessoryView = nil; //hides activity indicator
cell.userInteractionEnabled = YES;
cell.backgroundColor = [UIColor whiteColor];
UILabel *label1;
label1 = (UILabel *)[cell viewWithTag:1];
label1.textColor = [UIColor darkGrayColor];
UILabel *label2;
label2 = (UILabel *)[cell viewWithTag:2];
label2.textColor = [UIColor darkGrayColor];
//...etc
Inside my parser class's parserDidEndDocument method I am passing the NSArray back to the MainView.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K like %#",#"ISCHECKED",#"T"];
NSArray *filteredArray = [parsedDataArrayOfDictionaries filteredArrayUsingPredicate:predicate];
//call method in VieController1 to pass array over
SearchViewController *searchViewController = [[SearchViewController alloc] init];
[SearchViewController initFilterArray:filteredArray];
}
So then I head back over to my VC1 and the method that I have declared in the .h and then obviously in the .m file
and this is all of the code that I have in it.
#pragma - Reciver methods
-(void)initFilterArray:(NSArray*)array
{
//initalise array variable for use in latter views
FilterArray = array;
//reload to make cell white
[self.tableView reloadData];
// NSLog(#"%#", FilterArray);
}
While debugging the code the thread makes it to this method and runs everything.. if I uncomment that NSLog, it displays my filitered array and everything. However for some reason the reloadData dose not seem to call tableView:cellForRowAtIndexPath:.. I know this because I have debugged it with breakpoints etc.
So... hopefully this added information will help you help me :)
[self.tableView reloadData];
it will call the delegate methods.
if it isn't, you can check if self.tableView is linked to the tableView you want to reload.
Related
I've read some of the other posts but haven't found the right solution that seems to work in my case. I've got a UITableViewController class that is instantiated from the Main App Delegate and it controls other views that get pushed into the UINavigationController instance created from the App Delegate.
On the UINavigationToolBar I have a UISegmentedControl button that is split in two pieces, Factory Loads and User Loads. I have an action method attached to the SegmentedControl to detect when the button is pressed and that method is inside the UIViewController Class. My goal is to have the view reload with the contents of a new dictionary that is constructed from a plist file in the app bundle. I have the dictionary loaded, and the keys to be displayed set up in the respective data formats, but when I try to reload the UITableView, it crashes.
This is the last code I tried to get to work inside my action method, but it still doesn't function as intended.
- (void) action:(id)sender {
UISegmentedControl *segment = (UISegmentedControl *) sender;
if ([segment selectedSegmentIndex] == 1)
{
NSLog(#"User Load Selected");
NSString *userLoadFilePath = [[NSBundle mainBundle] pathForResource:#"UserLoads" ofType:#"plist"];
NSDictionary *userLoadDictionary = [[NSDictionary alloc] initWithContentsOfFile:userLoadFilePath];
NSArray *allKeys = [userLoadDictionary allKeys];
keys = allKeys;
dictionary = userLoadDictionary;
[[self tableView] reloadData];
}
else if ([segment selectedSegmentIndex] == 0)
{
NSLog(#"Factory Load Selected");
NSString *factoryLoadFilePath = [[NSBundle mainBundle] pathForResource:#"AlliantPowder" ofType:#"plist"];
NSDictionary *factoryLoadDictionary = [[NSDictionary alloc] initWithContentsOfFile:factoryLoadFilePath];
NSArray *allKeys = [factoryLoadDictionary allKeys];
keys = allKeys;
[[self tableView] reloadData];
}
}
I'm calling [[self tableView] reloadData] in an attempt to retrieve the actual table contained within the UIViewController to try to get the table to reload, but no luck. Any help is appreciated, and if more code is needed please ask. Thanks!
Andrew
------- Edit -----------
Here is the new code in reference to Chip's ideas. The issue is still not resolved and the app still crashes.
- (void) action:(id)sender {
// Create an instance of UISegmentedControl and set it as the sending event.
UISegmentedControl *segment = (UISegmentedControl *) sender;
// Detect which button was pressed and load a new dictionary appropriately
// Checking if userLoads was selected
if ([segment selectedSegmentIndex] == 1)
{
NSLog(#"User Load Selected");
keys = userKeys;
[[self tableView] reloadData];
}
// Checking if factoryLoads was selected
else if ([segment selectedSegmentIndex] == 0)
{
NSLog(#"Factory Load Selected");
keys = [dictionary allKeys];
[[self tableView] reloadData];
}
[segment release];
}
I would pre-load the dictionaries and hold them as ivars in the controller class.
I am betting that the loading of the dictionary contents is not complete, so you are in effect calling reloadData while the data is changing which is causing your crash.
Also, the read is an expensive option and you are loading each time the segementControl changes state.
I am having a problem with the SSCollectionView control, a subclass of NSTableView from the SSToolkit. For some reason, all delegates except for - (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath are called. Even though this delegate is #required, removing it will cause no exception either. And before you ask, yes the arrays below all have data in them.
I have checked if the data source/delegate becomes nil at any stage, but it doesn't, so I'm baffled.
Here's how I create the view:
- (void)viewDidLoad
{
NSLog(#"ViewDidLoad");
_titles = [[NSMutableArray alloc] init];
_subtitles = [[NSMutableArray alloc] init];
_thumbnails = [[NSMutableArray alloc] init];
_collectionView = [[SSCollectionView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.frame];
_collectionView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
[_collectionView setDelegate:self];
[_collectionView setDataSource:self];
[self.view addSubview:_collectionView];
[_collectionView reloadData];
}
This is called fine and the view appears - but with no data.
This method is never called:
- (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Delegate Called");
static NSString *const itemIdentifier = #"itemIdentifier";
SSCollectionViewItem *item = (SSCollectionViewItem *)[aCollectionView dequeueReusableItemWithIdentifier:itemIdentifier];
if (item == nil)
{
item = [[[SSCollectionViewItem alloc] initWithStyle:SSCollectionViewItemStyleImage reuseIdentifier:itemIdentifier] autorelease];
}
item.textLabel.text = [_titles objectAtIndex:indexPath.row];
item.detailTextLabel.text = [_subtitles objectAtIndex:indexPath.row];
item.imageView.image = [_thumbnails objectAtIndex:indexPath.row];
return item;
}
I don't think this is a bug - did I miss something?
This won't get called if there are no items in your collection view. Make sure the following SSCollectionViewDataSource method is implemented and returns a value greater than zero.
- (NSUInteger)collectionView:(SSCollectionView *)aCollectionView numberOfItemsInSection:(NSUInteger)section
Also, make sure you implement the following SSCollectionViewDelegate method as well so your items will be displayed correctly.
- (CGSize)collectionView:(SSCollectionView *)aCollectionView itemSizeForSection:(NSUInteger)section
I would recommend using SSCollectionViewController if it is the only view in your view controller since it will take care of a lot of the glue code for you.
Try to add your - (CGSize)collectionView:(SSCollectionView *)aCollectionView itemSizeForSection:(NSUInteger)section:before viewDidLoad.. don't know why but it works for me this way
I am using XCode's Navigation-based Application template to create an app that centers around an UITableView. I am having some problems deleting some rows in the tableView.
In all of the tableViews cells I have added a button by subclassing UITableViewCell like this:
TableViewCell.h:
#implementation TableViewCell
#synthesize button;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
RoutinesAppDelegate *appDelegate = (RoutinesAppDelegate *) [[UIApplication sharedApplication]delegate];
// Initialization code
button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
[button setFrame:CGRectMake(320.0 - 90.0, 6.0, 80.0, 30.0)];
[button setTitle:#"Done" forState:UIControlStateNormal];
[button addTarget:appDelegate action:#selector(routineDone) forControlEvents:UIControlEventTouchUpInside];
button.hidden = YES;
[self.contentView addSubview:button];
}
return self;
}
when the button is pressed the method "routineDone" in RoutinesAppDelegate.m is called.
The method looks like this:
RoutinesAppDelegate.m
-(void) routineDone
{
if ([[NSFileManager defaultManager] fileExistsAtPath:[self todayListPath]])
{
int indexToRemove = buttonIndex + 1;
todayListArray = [NSMutableArray arrayWithContentsOfFile:[self todayListPath]];
[todayListArray removeObjectAtIndex:indexToRemove];
[todayListArray writeToFile:[self todayListPath] atomically:YES];
}
}
This method removes an item from the array that the tableView loads. This works fine.
MY PROBLEM:
The cell is not removed from the screen, when the button is clicked.
In RoutinesAppDelegate.m I tried to use
[tableView reloadData]
but it does not seem to work from outside the RootViewController class.
It would be nice if I somehow could use the deleteRowsAtIndexPaths method, but I dont know how to call that method from outside commitEditingStyle in the RootViewController class.
So my question is really: How can make the cell go away? :)
Without seeing all the code it's hard to tell, but you can try adding after
[todayListArray writeToFile:[self todayListPath] atomically:YES];
[self.tableView reloadData];
Also, you don't have to retain that button, and probably don't have to have it as a property
in my tableview no of rows in section method is called and it returns value 17,but the cellforrowatindexpath is not getting called.i have put breakpoints in the first line of this method but this point is never shown when debugging,i have followed the tableviewdelegate and datasource.and the tableviews datasource,delegate are properly set in the Int builder.
i am also posting some of the code
in my viewcontroller.m
-(void)viewDidLoad
{
tweets=[[NSMutableArray alloc]init];
[self updateStream:nil];
[self.tweetsTable setDelegate:self];
[self.tweetsTable setDataSource:self];
}
-(NSInteger)tableView:(UITableView *)tweetsTable numberOfRowsInSection:(NSInteger)section {
return [tweets count];
}
-(UITableViewCell *)tableView:(UITableView *)tweetsTable cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"twitCell";
TwitCell *cell = (TwitCell *)[tweetsTable dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = (TwitCell *)[[[TwitCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.tweet = [tweets objectAtIndex:indexPath.row];
[cell layoutSubviews];
return cell;
}
cellForRowAtIndexPath won't get called if your tableview has height of 0
make sure your tableview always has VISIBLE rows
CGRect frame = self.tableView.frame;
frame.size.height = 100;
self.table.frame = frame;
It will not get called if you are returning 0 rows in numberOfRowsInSection method.
Please check the number of rows that you return.
Given the code you've shown us there only a few possibilities. Either self.tweetsTable is nil, tweets is nil, or tweets contains no element and count is returning zero. Now I know you say that everything is correct, but clearly something is up! You can add a bit of defensive code to detect these problems.
-(void)viewDidLoad
{
tweets=[[NSMutableArray alloc]init];
[self updateStream:nil];
NSAssert(self.tweetsTable, #"self.tweetsTable must not be nil.");
[self.tweetsTable setDelegate:self];
[self.tweetsTable setDataSource:self];
}
-(NSInteger)tableView:(UITableView *)tweetsTable numberOfRowsInSection:(NSInteger)section {
NSAssert(tweets, #"tweets must not be nil here");
NSUInteger n = [tweets count];
if(n == 0)
NSLog(#"WARNING: %# returning 0", __PRETTY_FUNCTION__);
return (NSInteger)n;
}
If you do this and one of the asserts fires you'll know where your problem is. If no assert fires then something is going on outside the scope of the code you have shown (e.g. something is getter released to soon or memory getting clobbered). Oh and one final thing -- can you see the empty table view on the screen? If you table is not visible AFAIK cellForRowAtIndexPath won't be called.
check this
or else use viewDidLoad code in viewWillAppear
Here is my function. After I placed "dispatch_async(dispatch_get_main_queue()..." inside of "tweets = [NSJSONSerialization.....", it works.
tweets = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&jsonError];
if (tweets) {
// We have an object that we can parse
NSLog(#"%#", tweets);
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
else {
// Inspect the contents of jsonError
NSLog(#"%#", jsonError);
}
In my cases I had created a UITableView as a property on my view controller, but I forgot to add it as a subview to self.view
Strangely you will get the symptoms that sujith described: numberOfRowsInSection will be called, but cellForRowAtIndexPath will not!
This was my missing line:
[self.view addSubView:self.myTableViewProperty];
And to defend against it:
NSAssert(self.myTableViewProperty.superview != nil, #"The table view dose not have a superview");
[self.myTableViewProperty reloadData];
You can refer following code to reload your tableView
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
In my case I was using custom subclass of UITableView and I have not called super super.layoutSubviews()
override func layoutSubviews() {
super.layoutSubviews() // Forgotten to call super implementation
self.layer.borderColor = Constants.primaryColor.cgColor
self.layer.borderWidth = Constants.primaryBorderWidth
self.indicatorStyle = .white
}
Let's say I have a property in my view controller, defined as follows:
#property (nonatomic, retain) UIImageView *checkmarkOffAccessoryView;
I #synthesize this in the implementation, release it in -dealloc and initialize it in -viewDidLoad as follows:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
So far so good.
When I use it in my table view delegate as an accessory view for multiple cells, two things happen:
Only one cell's accessory view shows the image
The application UI freezes.
The app doesn't crash, as near as I can tell, the UI simply becomes unresponsive. This is both in the simulator and on the device.
Here is how I use the initialized property with my cell:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = self.checkmarkOffAccessoryView;
else
cell.accessoryView = nil;
}
With the aforementioned code, only one cell shows the accessory view and the UI freezes.
If I initialize the UIImageView instance directly in the delegate method I get all condition-satisfying cells showing the accessory view and I do not experience the UI freeze:
- (UITableViewCell *) tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// initialize or dequeue cell...
if (condition)
cell.accessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
else
cell.accessoryView = nil;
}
My goal is to initialize as few objects as possible and reuse one UIImageView. I'm curious why the first chunk of code is problematic and what I could do to fix this.
It seems like the cell's accessoryView property should just increment the retain count of self.checkmarkOffAccessoryView but it appears I am missing some detail.
What have I overlooked? Thanks for your advice.
EDIT
I think that:
self.checkmarkOffAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
is the same as:
UIImageView *uncheckedView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmarkOff.png"]];
self.checkmarkOffAccessoryView = uncheckedView;
[uncheckedView release];
Either way, I experience the same freeze symptom.
You cannot add the same view multiple times. The UI handler will go bonkers. To make sure of this, I tried doing what you said above and I got the same issue. The UI freezes up, the image only appears for one of the cells.
The best thing you can do is to store your image as a UIImage allocated, and to have a helper function which returns a new UIImageView per cell.
Using your current method (without a stored UIImage) you might do:
-(UIImageView *) makeCheckmarkOffAccessoryView
{
return [[[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"checkmarkOff.png"]] autorelease];
}
And then do
cell.accessoryView = [self makeCheckmarkOffAccessoryView];
As you may be aware, UIImages on the other hand may be used any number of times. a UIImageView doesn't take up a lot of space, so you can easily have a bunch of those without worrying.
To expand on the one place only deal, imagine that you add a UIView to two places at the same time.
What will [ob removeFromSuperview] do for this object? Will it remove the view from both places? From one of them only? Which value will be returned when you request [ob superview]? Clearly the UI is not made to handle what you're asking for.
Try it without the autorelease in the initializer. I suspect you're over-releasing.
By the way, your console probably is showing a BAD_ACCESS error when it freezes. If you turn on NSZombieEnabled, my guess is you'll see it's making a call to a deallocated UIImage.
maybe this will help
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ShoppingListCell";
HSShoppingListCell *cell = (HSShoppingListCell *)[aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"ShoppingListCell"
owner:self
options:nil];
cell = shoppingListCell;
}
ShoppingListItem *theItem = nil;
theItem = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *selected = [UIImage imageNamed:#"listBullet_checked.png"];
UIImage *notSelected = [UIImage imageNamed:#"listBullet.png"];
cell.imageView.image = ([theItem.checkedOff boolValue] ? selected : notSelected);
cell.shoppingListLabel.text = theItem.productName;
[cell.shoppingListLabel setFont:[UIFont fontWithName:#"Marker Felt" size:26.0]];
return cell;
}
- (void)toggleCellImage:(NSIndexPath *)indexPath
{
ShoppingListItem *item = [self.fetchedResultsController objectAtIndexPath:indexPath];
item.checkedOff = ([item.checkedOff boolValue] ? [NSNumber numberWithBool:NO] : [NSNumber numberWithBool:YES]);
[HSCoreDataUtilities saveContext:item.managedObjectContext];
[self.tableView reloadData];
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self toggleCellImage:indexPath];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Reducing your case to the bare essentials (I was going to suggest to put two 'thin' UIView objects around the UIImageView...), I found that it is most probably impossible.
Create 2 empty UIView objects in IB, hook them up to bareView1 and bareView2. Then
UIImageView *imageView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"test.png"]];
[bareView1 addSubview:imageView]; // it shows either here ...
[bareView2 addSubview:imageView]; // ... or here
You can never get the image on sceen more than once like this. As a rule of thumb, I think the first object in line which does not inherit from UIView can be used multiple times, i.e. the UIImage. Like Kalle stated, a UIView can only have one parent in the view hierarchy.
Postponing the second addSubview only makes the UIImageView jump from bareView1 to bareView2.
The freeze happens maybe because the event handling gets mixed up: the accessory can be interactive, how would you know which one was tapped if they are one and the same object? So the code assumes objects are unique, and you manage to violate that assumption.