I'm trying to add a subview using storyboard. It's being displayed correctly, but there's no button (IBAction) in subview that works correctly, even with empty code, the app always crashes when clicked.
TableViewController.m
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Load SubView
SubviewViewController *subview = [self.storyboard instantiateViewControllerWithIdentifier:#"SubviewViewController"];
[self.view addSubview:subview.view];
}
...
SubviewViewController.m
- (IBAction)btnCloseSubview:(id)sender
{
// Crashes the app when clicked, even empty as it's.
}
You shouldn't just add another view controller's view to your view like that, it can get you into trouble. When I've tried that, it sometimes works and sometimes not. The proper way is to also add the controller as a child view controller of TableViewController:
SubviewViewController *subview = [self.storyboard instantiateViewControllerWithIdentifier:#"SubviewViewController"];
[self addChildViewController: subview];
[subview didMoveToParentViewController:self];
[self.view addSubview:subview.view];
You probably need to set the frame of subview's view as well (subview.view.frame = self.view.bounds if you want it to be the same size).
As an alternative, you could create a view, not a view controller, in a xib file, and add that as a subview of your view. In that case, you would set the file's owner of the xib to TableViewController, and put the button's method in TableViewController.
Look into UIContainerView, it handles sub-view controllers. You will need to handle your own communication between parent and child view controllers, but other than that it will work like you need it to for a sub-view, as long as your sub-view has the proper outlets and actions set up in the storyboard with it's view controller.
Have a look for more in-depth information on container views.
Related
Context: I am creating an IOS application in Objective C with the ArcGIS Runtime SDK. My root view controller is a map. I have the option to create spatial bookmarks and view them in a table view that is pushed over the view. I have a UISearchController on the table view to be able to filter out the bookmarks. If you select a bookmark without searching, you are automatically panned to that location. This is done by popping all view controllers, and loading the root view controller with a flag for the bookmark variable to set as the starting extent.
I would like this same functionality with the searchController table view.
However, I am either unable to dismiss the searchController and the bookmark is zoomed to, or the search controller is dismissed, and the user is brought back to the initial table view.
What I have so far:
In the didSelectRow method on the searchController TableView:
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//Accept the spatial envelope and save it to the shared variable to be passed to main view controller
//Now dismiss the searchController and call method to zoom to bookmark
[self.navigationController dismissViewControllerAnimated:NO completion:^{
//reference to shared instance method
[[BookmarksTableViewController sharedBookmark] zoomToBookmark];
}];
In BookmarksTableViewController.m
-(void)zoomToBookmark{
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:#"viewController"];
[self.navigationController pushViewController: myController animated:YES];
//clear the navigation stack
self.navigationController.viewControllers = [[NSArray alloc] initWithObjects:myController, nil];
}
To me it seems like this should work, but all that happens is the UISearchController is dismissed, then the table view is not dismissed, and I am back where I was before I entered search text.
Any thoughts on a better way to dismiss the UISearchController or how to get back to my root view controller?
Hope you mean to this answer
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissViewControllerAnimated:NO completion:nil];
}
This has been a headache for few hours now and I finally found out what is actually happening, but I don't know how to solve this issue.
I've got List.h with UITableView properly connected from storyboard:
#property (strong, nonatomic) IBOutlet UITableView *tableView;
Then there's List.m where I set delegates and datasource for my UITableView:
// Set tableview datasource and register class for cell reuse
self.tableView.dataSource = self;
[self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:#"cell"];
// Set tableview delegate
self.tableView.delegate = self;
// Set tableview cells style
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = [UIColor colorWithWhite:0.1f alpha:1.0f];
// Set tableview frame
self.tableView.frame = CGRectMake(0.0, 35.0, self.tableView.frame.size.width, self.tableView.frame.size.height-35.0);
Then on NSNotification I'm trying to [self.tableView reloadData]:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doUpdateAppBefore:) name:UIApplicationWillEnterForegroundNotification object:nil];
Also I'm reloading data of tableView on every UIApplicationBecomeActive notification.
Before I close my app and open it up from background to find out if it's reloads the data, I'm moving to another UIViewController and going back, which causes that somehow identifier of my self.tableView changes. I'm checking it in:
NSLog(#"Calling reloadData on : %#");
and at the beginning it gives me:
Calling reloadData on <UITableView: 0x9b09400;....
but after I segue back from another UIViewController it gives me:
Calling reloadData on <UITableView: 0x9b4b000;
which causes that it doesn't actually reload the data after I open up the app from the background state.
I've been thinking... when I segue back from another viewcontroller, viewDidLoad fires again, is it possible that it somehow sets tableView.delegate again and changes something? Just thinking...
Thank you very much for your answers.
It sounds like you have a view controller, push a modal view on top of it, and then want to go back to the original view controller when you're done. So, you set up a modal segue in your storyboard, and then a second modal segue to go back. The problem is that your second modal segue doesn't return to the original view controller, but it creates a new instance of that view controller, and now your have the original view controller, the second view controller, and an unwanted third view controller. Instead of creating a segue, which creates the third view controller, you need to dismiss the second view controller, which then gets you back to your original view controller, and therefore also your original table view. So what you want to do is get rid of the second segue and replace it with an IBAction, put something like
- (IBAction)goBack:(id)sender;
in your .h file. Connect that to your button or whatever you're using to trigger the segue now. Then, in your implementation file, dismiss the modal view like so:
- (IBAction)goBack:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
That should get you what you want.
Alternately, and this is probably better, you can use an unwind segue as well. Go to List.h, and create this method:
- (IBAction)unwind:(UIStoryboardSegue *)segue;
Then just implement it, you can leave it blank for now. Then, drag from the button that currently triggers the segue to Exit, and select the Action Segue unwind. That will also get you back.
I've spent hours and I cant figure this out. I have a detail view controller (UITableView) which is launched here:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
EventLocationDetailController *newDetailViewController = [[EventLocationDetailController alloc] initWithNibName:#"EventLocationDetailController" bundle:nil];
self.eventDetailController = newDetailViewController;
[self.navigationController pushViewController:self.eventDetailController animated:YES];
[newDetailViewController release];
}
In the detail view controller there is a button method which calls the below method to display a slide-in-slide-out animation confirming the users choice:
-(void)confirmLastActionWithMessage:(NSString *)message {
ConfirmActionViewController *newConfirmActionViewController = [[ConfirmActionViewController alloc] initWithNibName:#"ConfirmActionViewController" bundle:nil];
self.confirmActionViewController = newConfirmActionViewController;
[newConfirmActionViewController release];
[[self.view superview] addSubview:self.confirmActionViewController.view];
}
Protocol method called by the ConfirmActionViewController indicating that the animation is finished.
-(void)didFinishConfirmDisplay:(UIView *)viewToRemoveFromSuperview {
[viewToRemoveFromSuperview removeFromSuperview];
}
This works perfect the first time I press the button. If I pop the detail controller and push it back on to the stack and press the button again, nothing happens and the detail controller's superview is nil every time I invoke the method after that. Superview is not nil in the viewWillAppear method for the detail view, only when It gets to the confirmLastActionWithMessage method. No other user interaction happens in between. How do I get the superview back? I have a similar code that works without animation.
I've also noticed that the detail view controller hasn't called dealloc when popped off the stack. Not sure where the problem is.
Thanks.
EDIT 1
OK. I replaced the addSubview line with this one:
[self.view insertSubview:self.confirmActionViewController.view atIndex:0];
and the animation view appeared underneath one of the table cells. Could one of the table cells steal the superview?
Well I don't really understand why you should add the subview to the superview. Why not add it just to self.view
I may not be able to explain why there is no superview but try either adding the controller view to self.view or
[[[[UIApplication sharedApplication] delegate] window] addSubview:yourview];
This will render the view on top of everything.
This one is probably something simple, still learning the ins-and-outs on this but I've run out of searches for this one with no available answer.
I've got a UIViewController with several elements displayed on it, one such element is a UITableView. The UITableView has it's own class and is allocated in the UIViewControllers viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
UITableView *insideTableView = [[UITableView alloc] init];
tableView.delegate = insideTableView;
tableView.dataSource = insideTableView;
}
Everything is working fine in regards to the tableview. Today I am experimenting with a few additions, one of which is a new view popup on cell selection within that tableview.
Inside my TableView Class, I have the following:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Cell Pressed, Present View");
PopupView *popupView = [[PopupView alloc] initWithNibName:#"PopupView" bundle:nil];
popupView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:popupView animated:YES];
}
Now it gets called fine, verified by the NSLog, however the view doesn't appear. I know the problem is related to the fact that I want PopUp to appear over the TableViews Parent rather than itself.
I'm just not sure how to properly call it in this instance.
The delegate is a UIViewController which doesn't have its view property set, which is why presentModalViewController:: doesn't work.
You need the view controller containing the table view to present the modal view controllers, but note that that view controller is not the parent of the table view delegate. This is because you have no view controller hierarchy in place.
The easiest way to fix this is to put those methods inside the view controller whose view contains the table view. Alternatively the table view delegate needs to hold a reference to the view controller so it can call presentModalViewController:: on it.
The latter approach can lead to retain cycle, so you have to use a non-retaining reference. The nicest implementation is the delegate pattern.
Also, you don't want to do the instantiation in viewWillAppear: because that can be called multiple times during the lifecycle of a view controller. Put the code in viewDidLoad and balance it in dealloc. Right now you are leaking memory every time your view appears, which when your modal view controller is working will be every time the modal view controller is presented and dismissed.
I'm creating a navigation-based app which displays a graph, rendered with openGL, and a tableview listing disclosure buttons of all of the elements that are displayed on the graph, and a settings disclosure button.
The navigation controller is also a tableview delegate and datasource, and the tableview is added to the view programatically and has its' delegate and datasource set to 'self'. The OpenGL based graph view is added via IB.
The problem I'm having is that I'm trying to push a view controller (either settings or graph element properties) within the didSelectRowAtIndexPath method. The method registers and the new view is pushed on, but the tableview stays and obscures part of the view that was pushed on, as if it has a different navigation controller.
I can't seem to set the tableview's navigation controller to be the same as the rest of the UINavigationControllers' view.
Does anyone know how I could fix this?
My navigation controllers' initWithCoder method, where the tableview is added, appears as follows:
elementList = [[UITableView alloc] initWithFrame:tableFrame style:UITableViewStyleGrouped];
elementList.dataSource = self;
elementList.delegate = self;
[self.view addSubview:elementList];
Further in the source file, the DidSelectRowAtIndexPath method where the navigation controller is pushed appears as follows:
Settings* Controller = [[Settings alloc] init];
[self pushViewController:Controller animated:YES];
[Controller release];
Fixed by just adding a UITableView in IB, adding IBOutlet to elementList, and setting the UIViewController as the delegate and datasource via IB.
Stack Overflow can be really useful for putting your problems to words so the solution becomes obvious.