I have a UIViewController call Parent, and I have a UIView subview within Parent. I want to add one of two different possible UIViewControllers, called A and B, as subviews of Parent. A is a UIViewController with a UITableView. I set the datasource and delegate of the UITableView in A to A.
I can then "successfully" add A to Parent, setting the data for A as follows:
AViewController *vc = (AViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"A"];
NSMutableArray *data = [#[#"foo",#"bar",#"baz"] mutableCopy];
vc.posts = data;
[self.container addSubview:vc.view];
By successful, I mean that I see the tableview with the correct data in the cells. Namely foo, bar, and baz as the rows.
My Problem: When I try to scroll the tableview, it crashes. When I try to select a cell, I get the following exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '-[_UIAppearanceCustomizableClassInfo
tableView:didSelectRowAtIndexPath:]: unrecognized selector sent to instance 0x78c64430'
Because the AViewController is declared locally in your code above, it is deallocated as soon as that code completes. So when you touch for scrolling/selection and the delegate/datasource methods are called, the delegate and datasource point to a completely different object (or none at all). Hence your crash.
Furthermore, when implementing customer container views you need to implement some code so both parent and child know. Take a look at "Implementing a Custom Container View Controller" in the Apple Docs:
[self addChildViewController:vc];
[self.container addSubview:vc.view];
[vc didMoveToParentViewController:self];
I believe the addChildViewController will also provide a strong reference from the parent to the child (vc), thereby preventing it from being deallocated. So the above code should fix the deallocation problem as well.
Related
I am trying to create a PageViewController with multiple horizontally-paging UITableViews inside. The UITableViews are stored in an array; I use the UIPageViewController setViewControllers method to add the very first object in the array to the PageVC:
[self.pageViewController setViewControllers:[self.tableViews objectAtIndex:0] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
Then I add the remaining UITableViews to the PageViewController by using the viewControllerBeforeViewController and viewControllerAfterViewController methods.
However the setViewControllers method is producing the following error:
-[UITableView count]: unrecognized selector sent to instance 0x7c347e00
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableView count]: unrecognized selector sent to instance 0x7c347e00'
Why is this error happening?
You should have a look at the method first you are using here:
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers
direction:(UIPageViewControllerNavigationDirection)direction
animated:(BOOL)animated
completion:(void (^)(BOOL finished))completion;
It says the parameters should be the viewControllers
Parameters:
viewControllers The view controller or view controllers to be
displayed.
So, you have to take a UIViewcontroller or UITableViewController.
Two problems.
You need to pass view controllers and not views to the page view controller. For each UITableView, you need a UITableViewController.
If you change your variable self.tableViews to self.tableViewControllers (with the UITableViewController you create for each UITableView) you're half way there.
The other problem is that the first parameter for setViewControllers should be an NSArray. (NSArray has a selector called count.)
Change your code to wrap the first table view in an array when you're adding it:
[self.pageViewController setViewControllers:#[ [self.tableViewControllers objectAtIndex:0] ] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil]
I have a controller named as "firstViewcontroller" where i have a UITableView named as "discoveredInstanceTableView". I want to load that UITableView in to another UIViewController named as "secondViewcontroller"
I have used the below code but it is not working, It says property "discoveredInstanceTableView" not found ...Anybody please help me:
In the firstViewcontroller:
IBOutlet UITableView *discoveredInstanceTableView;
In the Secondviewcontroller:
firstViewcontroller *vc1 = [[firstViewcontroller alloc]initWithNibName:#"firstViewcontroller" bundle:nil];
[self addChildViewController:vc1];
[self.myTableview addSubview:vc1.discoveredInstanceTableView];
What you asked is valid only if you curious to know why the above thing is not working answer would be
You are doing something that is not allowed, this can not be done as per the documentation.
However, If we forget about the right wrong approach, you probably adding a table view as a subview over a table view itself and I am sure you passing a table view to a table view which might not be allocated.
First think about the UITableView how it works? it simply a ScrollableView which display content over its cells.
Eventually would recommend you read about TableView
EDIT: From the Above Comments
IMPORTANT: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.ยป As UITableView is a UIScrollView, this applies here as well.
Possible Alternatives of displaying TableView inside the SecondViewController
Use #Rajath Kornaya's Answer And In my opinion that is not right approach since whenever you required callback action like on cell tap, you want to display an alert(or something else), you can't get the delegate callback inside the SecondViewController
But there are so many other right approaches available, that you should follow up.
Create a TableView separately either programmatically or through the XIB/Storyboard
Add delegate and data source (methods which responds when something interesting happened e.g Cell going to populate called cellForRowAtIndexPath) to current SecondViewController
Define all required data source methods and write proper code.
If you required to do something on cell tap, add specific delegate method too.
But if you want to reuse the FirstViewController Class TableView simply create a CustomView and add TableView inside there and simply add that view to each view controller class.
I hope it may helps you!!!
declare in viewcontroller2
#property (nonatomic, strong) UITableView *table;
create table in viewcontroller1
tableView=[[UITableView alloc]initWithFrame:CGRectMake(10, 10, 250, 300) style:UITableViewStylePlain];
[self.view addSubview:tableView];
tableView.backgroundColor=[UIColor greenColor];
while calling viewcontroller2 pass table to viewcontroller2
ViewController2 *v2=[[ViewController2 alloc]init];
v2.table=tableView;
UINavigationController *navigation=[[UINavigationController alloc]initWithRootViewController:v2];
[self presentViewController:navigation animated:YES completion:nil];
in viewcontroller2 access the table using the global variable
[self.view addSubview:self.table];
This is puzzling me.
The context
The original tutorial I'm following.
Where the segue is added to the Main View via a custom segue:
- (void) perform {
MainViewController *source = (MainViewController *)self.sourceViewController;
UIViewController *destination = (UIViewController *) self.destinationViewController;
for(UIView *view in source.main.subviews){
[view removeFromSuperview];
}
source.currentViewController = destination;
destination.view.frame = CGRectMake(0, 0, source.main.frame.size.width, source.main.frame.size.height);
[source.main addSubview:destination.view];
}
The TextField is connected as delegate in the child View Controller. All things being equal I get the app crashed without any message.
The workaround
In the Main View Controller, in -(void)prepareForSegue: I've added [segue.destinationViewController setDelegate:self]; in the meantime I've added a property in the child View Controller id<UITextFieldDelegate> delegate and modified the textfield delegate as self.delegate.
This works, but the trouble is that I've to set the delegated methods in Main View Controller which is not quite efficient as I have more View Controllers to add.
The Objective
How do I set each View Controller to be the delegate for itself without crashing?
The immediate cause of your error is that the view controller that your views belong to is being deallocated. The fact that your views are on screen while their view controller is deallocated highlights a fundamental flaw in the approach of taking views off one view controller and adding them to another. View controller containment is the correct way to solve an issue like this.
Changing the currentViewController property to strong will fix the memory management issue you're seeing, but it's just a bandaid. Your currentViewController will still be missing rotation methods, appearance and disappearance methods, layout methods, and so forth. View controller containment ensures these methods get called for the view controller whose views are on screen.
Here is an altered version of your project that illustrates how to use view controller containment. I think that will be a better solution than manually removing and adding subviews of the view controllers themselves. See the Apple docs for more info on custom view controller containers.
At first, let's see crash report. Please, do the following:
1. Add Exception Breakpoint
2. Edit it as in the picture
You should create a custom class for the destinationViewController wich will implement UITextFieldDelegate
#interface DestinationViewController <UITextFieldDelegate>
#end
And from storyboard add the class to UIViewController that has TextField
And make the connections for elements and TextField delegate.
Implement delegate methods.
You will not need the implementation of prepareForSegue: anymore. You will have two different classes with different elements. Only if you need to pass something from source to destination then you use prepareForSegue:
Hope you'll understand
In my storyboard I have 2 views one master and one detail with list. I want to show detail view on master. When I add detail to master like that:
- (void)viewDidLoad
{
[super viewDidLoad];
tableViewController = [[TableViewController alloc] init];
[self addChildViewController:detailVC];
[self.view addSubview:detailVC.view];
}
I get crash like that:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
Im sure that there is everything ok with my table viewController becouse when I set it to rootViewController there is everything ok. What I'm doing wrong?
Looks like its an issue with tableview delegate methods. In story board please use static cell for your table view controller and remove table view delegate methods or else implement all required table view delegate methods.
I have a UIViewController with a container view which holds a UITableView.
Within the UITableView I have a NSFetchController with NSPredicate that uses variables from the intial UIViewcontroller (I hope your still with me). For one instance that the user changes the container view does not update.
How can I force a reload/update on the container view when this occurs? I looked around but did not see much on this topic.
To be specific I have a UIDatePicker that changes the date on a button. It is this that needs to be updated.
From the initial UIView controller, you can get a reference to the table view controller with self.childViewControllers[0]. So, you need to do it like this:
UITableViewController *tbc = (UITableViewController *)self.childViewControllers[0];
[tbc.tableView reloadData];
You wrote that you have UIViewController there and not UITableViewController - if you have UIViewController you have to have a property there. I assume that here everything is ok.. But check.
What I am doing while working with containers views is adding element by:
_someVC = [self.storyboard instantiateViewControllerWithIdentifier:#"SomeVC"];
[self addChildViewController:_someVC];
_someVC.view.frame = _containerView.bounds;
[_containerView addSubview:_someVC.view];
[_someVC didMoveToParentViewController:self];
And not with IB. Hope that helps.