I created a Master-Detail-Application, which uses one DetailViewController and multiple TableViewDataSources. Every time the user touches an item, i check the items class and choose the right TableSource for it.
Just like this:
if ([_detailItem isKindOfClass: [cAdress class]]) {
self.dataSource = [[AddressDetailTableSource alloc] init];
((AddressDetailTableSource *) dataSource).current = _detailItem;
} else if ([_detailItem isKindOfClass: [cActivities class]]) {
self.dataSource = [[ActivityDetailTableSource alloc] init];
((ActivityDetailTableSource *) dataSource).current = _detailItem;
}...
Sometimes i go more into Detail and push a new DetailView above the current DetailView. I do this a lot with some different views. Choosing an item in the MasterView causes, that the application goes back to the first DetailView (popToRootViewController).
I now have a problem with one view in particular. When this view is on Top and i choose an item in the MasterView, my App crashes. With NSZombies i found out, that it still tries to build the table with the wrong DataSource. Or at least it tries to call "titleForHeaderInSection" on the wrong DataSource. The error message is:
[ItemDetailTableSource tableView:titleForHeaderInSection:]:message sent to deallocated instance...
The error only occurs with this specific TableSource, also i treat same all the same.
Can anyone help me to get rid of this problem?
Any help is appreciated!
I think your app is trying to access datasource using deallocated instance, you better have individual classes for each tableView, it will simplify your work, always try to modulize the classes instead of trying to put everything in just one class.
Related
So I am implementing a custom navigation item in my view controller via the method like this
-(UINavigationItem*)navigationItem{
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
return item;
}
The SearchNavigationItem will set itself up, add a UITextField and so on.
The field.delegate will have the item as the delegate.
So the issue I have is that when I try to grab the text of the field, it is nil. But when the "textfield changed" is called, I can access the field via the argument (textFieldDidChange:UITextField*) and it has the text.
Another issue, like the title, was that when I did [field resignFirstResponder] nothing happened.
Okay so I already have the answer, and I am writing this question because I could personally not find any help while fixing it.
So the issue is that navigationItem can be called multiple times and this kept creating new bars.
So the solution became, simply, this:
-(UINavigationItem*)navigationItem{
// Apparently it should be treated as a 'singleton' which I think it says
// kind of in the documentation. This comment is just to reinforce that
// it burned me to init it each time this method is called. Which is can
// be multiple times and also outside of the class itself (like when nav'ing)
if(item == nil){
item = [[SearchNavigationItem alloc] init];
item.delegate = self;
}
return item;
}
Hope this helps someone else.
I am trying to reload data in a tableview based on a users account permissions whenever they log in.
The two classes involved in this are:
mainViewController and menuViewController
Currently I am able to use
[self.tableView reloadData];
To reload the data when called within the viewWillAppear method. Which is no good for me since the user hasn't logged in when the view loads so there is no data to populate the table at this point.
I have created a method called populateTable in menuViewController.h which I am calling in the mainViewController.m file on button press using the following;
(IBAction)Reload:(id)sender {
menuViewController *mvc = [[menuViewController alloc]init];
[mvc populateTable];
}
This seems to work correctly as I have an NSLog within the populateTable method which executes. However the reloadData does not work.
Here is my populateTable method;
-(void)populateTable {
self.section1 = [NSMutableArray arrayWithObjects:#"test settings", #"test", #"test",#"Users and access",#"No-track IPs", nil];
self.section2 = [NSMutableArray arrayWithObjects:#"Rules", #"Channels",#"Goals",#"Pages", nil];
self.menu = [NSMutableArray arrayWithObjects:self.section1, self.section2, nil];
[self.tableView reloadData];
NSLog(#"Reloading data");
}
Can you guys help me out here, I have been staring at this all day and getting nowhere, thanks!
From my experience this is likely a problem with timing - the IBOutlet of self.tableView is not ready when you call reloadData on it (add an NSLog and see for yourself - it is nil when called).
To solve this, the populateTable method must be called within the UIViewController's viewDidLoad method. This guarantees that the outlets are not nil and that everything is ready for your data population.
Also, you should not instantiate your MenuViewController with [[MenuViewController alloc] init] but using the storyboard's instantiateViewControllerWithIdentifier.
Your problem is this line,
menuViewController *mvc = [[menuViewController alloc]init];
This creates a new instance of menuViewController, not the one you see on screen. You need to get a reference to the one you have, not create a new one. How you get that reference depends on how, when, and where your controllers are created.
This is used to work fine for my pre-ARC code, but since refactoring all the project to be ARC compatible, I start getting this crash:
[CustomViewController respondsToSelector:]: message sent to deallocated instance
My project is an iPad app with a split view, but contrary to apple documentation, previous developer has put another view controller to be visible on app launch before split view. So I know this is not the right way to do, but as I said it used to work before ARC integration so I need to get a workaround with this.
The root view controller which contain a menu of items, each item display a detail form to be filled, then a click on next button move to the next detail screen, etc.
The issue starts when I click on home button put on root view to get back to the home view, here is the relevant code that move the user to the home screen:
//this method is in the appdelegate, and it gets called when clikc on home button located on the root view
- (void) showHome
{
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
Then when I click on a button to get back to the split view (where are the root/details view), the app crashes with the above description. I profiled the app with instruments and the line of code responsible of that is located in the RootViewController, in the didSelectRowAtIndexPath method, here is the relevant code:
if(indexPath.row == MenuCustomViewController){
self.customVC=[[CustomViewController alloc] initWithNibName:#"CustomVC"
bundle:nil];
[viewControllerArray addObject:self.customVC];
self.appDelegate.splitViewController.delegate = self.customVC;
}
customVC is a strong property, I tried to allocate directly and assign to the instance variable but that didn't help to fix the crash. Any thoughts ?
EDIT:
Here is the stack trace given by instruments:
[self.appDelegate.splitViewController setViewControllers:viewControllerArray];//this line caused the crash
[viewControllerArray addObject:self.appDescVC];//this statement is called before the above one
self.custinfoVC=[[CustInfoViewController alloc] initWithNibName:#"CustInfo" bundle:nil];//this statement is called before the above one
self.appDelegate.splitViewController.delegate = self.appDescVC;//this statement is called before the above one
custinfoVC and appDescVC are strong properties.
I solved this problem by setting my delegates and datasources to nil in the dealloc method. Not sure if it'll help you but its worth a try.
- (void)dealloc
{
homeController.delegate = nil;
//if you have any table views these would also need to be set to nil
self.tableView.delegate = nil;
self.tableView.dataSource = nil;
}
You may want to setup the CustomViewController during app launch, and display the other views modally on top if necessary. The error message you're getting is because something is getting released by ARC prematurely. It might have not manifested before because pre-arc stuff wasn't always deallocated immediately. ARC is pretty serious about releasing stuff when it leaves scope
Hard to tell without seeing a lot more of the code involved, and what line it breaks on, etc.
This:
- (void) showHome {
//THIS: where is homeController allocated?
homeController.delegate = self;
self.window.rootViewController = homeController;
[self.window makeKeyAndVisible];
}
EDIT:
Add this line right above the line that causes your crash
for (id object in viewControllerArray) {
NSLog(#"Object: %#",object);
}
I had the same Problem.If you are not using "dealloc" method then use "viewWillDisappear" to set nil.
It was difficult to find which delegate cause issue, because it does not indicate any line or code statement for my App So I have try some way to identify delegate, Maybe it becomes helpful to you.
1.Open xib file and from file's owner, Select "show the connections inspector" right hand side menu. Delegates are listed, set them to nil which are suspected.
(Same as my case)Property Object like Textfield can create issue, So set its delegate to nil.
-(void) viewWillDisappear:(BOOL) animated{
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController]){
self.countryTextField.delegate = nil;
self.stateTextField.delegate = nil;
}
}
My application is using JASidePanels libraries and I had set them using storyboard. The center view is a UITableView and the left panel is a view in which I call a method (by pressing a button)
- (IBAction)reloadAllFilters:(UIButton *)sender{
MasterViewController *masterController = [self.storyboard instantiateViewControllerWithIdentifier:#"centerViewController"];
NSMutableArray *arrayOfFilterIds = [[NSMutableArray alloc] init];
// another code
masterController.filterIdsToDisplay = arrayOfFilterIds;
[masterController.tableView reloadData];
[self.sidePanelController toggleLeftPanel:self];
}
The thing is, that when the app starts, the number of rows returned by number, but after I call this method, the number of rows is 30 but I cant see nothing.
I think that the problem is that I am instantiating a new masterController and therefor I cant get the actual table view to be displayed? Or am I wrong? Can you please help me?
Yes you are right. You create a new master controller every time you call the method
From UIStoryboard reference:
This method creates a new instance of the specified view controller each time you call it.
You need to get the existing one.
Please set the delegate and datasource to your tableview
like this ...
tableView.delegate = masterController;
tableView.dataSource = masterController;
I'm working with tableviews showing a hierarchy data structure. I take the first node and show the sons in the tableview and repeat it until the end of the tree. I'm doing it in this way:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if([[actualNode getSonAtIndex:0] sonsCount]>0) {
NSLog(#"New level");
actualNode = [actualNode getSonAtIndex:indexPath.row];
[self.tableView reloadData];
} else {
NSLog(#"Service");
[self performSegueWithIdentifier:#"Service" sender:NULL];
}
It is not the correct way but the problem is that I don't know how many levels has the tree so I can't create them in the Storyboard. The idea is create only one TableView for showing each level but doing it in that way I can't go back to the previous level on the NavBar like I could do if I was working with some controllers in the storyboard and I don't have animations. So, is there any way to do it? something like:
MyNextLevel *nextlevel = [MyNextLevel alloc];
[nextlevel setNode: actualNode]
Myactualtableview = nextlevel; (Here the next level is showed in the screen with animations and with the possibility to go back to the previous level)
Thanks.
This is why I don't like storyboards. It has always been possible to do what you want to do (if I'm understanding it correctly), but storyboards detract from it; they don't have the flexibility of doing things in code, which is what we always had to do back in iOS 3 and iOS 4 anyway.
Anyhow, you want to do something like this:
UITableViewController* tvc = [[MyTableViewController alloc] initWithNibName:#"MyNib" bundle:nil];
[self.navigationController pushViewController:tvc animated:YES];
Your business logic can just pick the class for MyTableViewController. Alternatively, MyTableViewController could be something flexible, where between those two lines you give it some configuration info that causes it to show the right data! (I have to admit, though, that you could do that same thing using a storyboard, configuring the table view controller in performSegue:.)