I have a UIViewController (named VC) that inherits from UITableViewDelegate and UIScrollViewDelegate. The previous UIViewController loads VC like this:
[self.view addSubview:VC.view];
which means viewWillAppear doesn't fire. I can add that method just after the above line:
[VC viewWillAppear];
but then it will fire before cellForRowAtIndexPath, which results in an empty tableview reference when I try to scroll.
I would like to scroll to a particular row in the table on load of VC. But because I don't know when the tableview's cellForRowAtIndexPath has completed (lack of viewWillAppear), I don't have any place to put the scroll code. I already keep a reference to the tableview and can use it for scrolling. But where can I place the scroll code?
Do you mean that your UIViewController conforms to the UITableViewDelegate and UIScrollViewDelegate protocols rather than 'inherits'?
You could invoke [VC viewWillAppear:] directly from your parent view controller. If the view handled by VC is always a sub-view of another view controller's view then do you really need a full UIViewController instance to back your UITableView? You could just provide a delegate and datasource implementation for the table view.
You could then instantiate your delegate and datasource either in the NIB that defines the table view (by adding an object in Interface Builder) or in the viewDidLoad method of the view controller that is currently the parent to VC.
Surely when the new view controller's view is added and becomes visible, it will call the viewDidAppear rather than the viewWillAppear. I'd stick the scroll code in there.
If that still doesn't work then try a delay in your init method. Something like this:
[self performSelector:#selector(myScrollingFunction) withObject:nil afterDelay:2.0];
You could play around with the delay time to get something that suits, although your view controller's view may not always appear after the same amount of time.
Related
I have a UIViewcontroller on which I have added a UIView as a subview and then added another UIView as another subview over the first view.
Now I want to call a method in the UIViewController from the last UIView. All of these views are custom views and have been created as different classes.
What would be the best way to call this topmost view from the child of the child?
You cannot add a view as a UIViewController's subview. I believe you added it as a subview of the controller's view. I do not understand what you mean to ask in the question. Please correct me if I'm wrong.
I assume you want to call a method in the UIViewController on some user interaction. If this view (the child of child)is a button, you could simply do this:
[button addTarget:nil action:#selector(methodToCall:) forControlEvents:UIControlEventTouchUpInside];
A nil target will result in the method methodToCall in the UIViewController to be called.
If you don't see a call there, make sure the view hierarchy (all predecessor views in it) is user interaction enabled.
A better approach (If you haven't subclassed UIButton) would be to create a protocol for your custom views, specify a tap gesture recognizer, specify a delegate and send call a method in the delegate whenever you receive a tap.
The whole point of a view controller is that it's in control of the view. You shouldn't be adding knowledge to the view which requires it to know to pass information to the controller. The view should either have UIControls on it and the controller sets the appropriate target and action or the controller should add gestures to the view and, again, specify appropriate target and action.
I can not get what you want exactly..if you want simple topmost view then you set tag of this view when you call addsubview .and every time increment tag when add subview and decrement tag when remove subview.then you easily get topmost view with help of current tag.
Do like this,
In TopView.h
#property (nonatomic, assign) YourViewController *ViewController_object;
In TopView.h
-(void)dealloc {
[super dealloc];
ViewController_object=nil;
}
-(void)your_method {
[ViewController_object method_from_VC];
}
hope it will helps you...
Let's say I have a viewcontroller "ViewBViewController". In that viewcontroller I create an instance of the "ViewAViewController" and use the addSubView: method to display this ViewAViewController. It then processes a bunch of information, and is now done. I want it to automatically get removed as a subview when it's done.
I was looking at the removeFromSuperview method, but can't seem to call that from within the viewcontroller whose view I'm trying to remove (my first instinct was [self.view removeFromSuperview], but that gets rid of the entire view, not just the subview I'm after).
The only way I can think of is setting up a delegate protocol, and have View B take care of the unloading of View A on behalf of View A as its delegate. However this approach seems a bit overkill. Am I missing an easier solution?
Thanks in advance!
UIViewController does not respond to removeFromSuperview, because a UIViewController is not a UIView but a UIViewController. No surprises there. You can call removeSuperview on any view, such as the view associated to a view controller (here self):
[self.view removeFromSuperview];
or if you just want to remove one subview:
[mySubview removeFromSuperview];
or if your subview is a member of self (i.e. declared in the interface say):
[self.mySubview removeFromSuperview];
Have you tried: setHidden: YES ?
There are two basically correct solutions here:
Use a navigation controller. You can hide the navigation bar if you don't want it to be part of your interface. Then you can dispose of the top view controller and its view from either view controller by calling
// argument can be YES or NO, as you like
[self.navigationController popViewControllerAnimated:YES];
Use a delegate call that tells the parent view controller to do something like
-(void)removeViewA {
// remove the view from the view hierarchy
[self.viewAController.view removeFromSuperview];
// dispose of the view controller so it doesn't leak.
self.viewAController = nil;
}
It's important to make sure that you don't leak the child view controller and its view.
Either of these approaches works, but using a navigation controller seems more idiomatic to me.
I have UINavigation Controller that has a UIViewController pushed onto the stack and displayed.
That UIViewController has a UITableView and uses an external class for that UITableView's datasource and delegate. I do this because I need to swap that datasource dynamically. Everything works beautifully for displaying data.
However, when handling didSelectRowAtIndexPath "down" in the delegate
I'm struggling with the proper way to call all the way back to the UINavigationController. Whereas I'm used to being able to do something like this when the UIViewController and Delegates are the same object:
ExampleViewController *newViewController = [[ExampleViewController alloc] init];
[self.navigationController pushViewController:newViewController animated:YES];
[newViewController release];
I cannot do so from the delegate and I'm just not getting what the correct reference back to the UINavigationController should be is the pushViewController.
Do I need to set an ID similar to a delegate so that the UITableViewDelegate has a pointer directly back to the UINavigationController?
Only UIViewController instances that have been pushed onto a navigation stack have convenient access to a corresponding UINavigationControllerĀ instance through the "navigationController" property. So you have a couple of options:
1) Pass a reference to the navigation controller to your table view delegate(s) so they can call the "push" method you're used to using.
2) Pass a reference to your view controller to your table view delegate(s), and have them call a method when a user taps a table view row. You can write your own delegate protocol and implement it in your view controller to formalize this approach.
I have a view in my app that displays a UITableView. This view is created in a nib file and has a custom view controller. The UIViewController subclass for this view acts as the Datasource and Delegate for the UITableView.
My UITableView displays several rows based on my data. Then, the last row displays different text: "Add another...". If the last row is selected, I present a modal view controller (to allow the user to add more data). When I dismiss the modal view controller, I again see the original view (as expected) and all appears to be well. However, when I try to interact with this view, the app crashes.
From placing several NSLog() statements through the UIViewController (for the UITableView), I have determined that the -dealloc method is being called just after the modal view is dismissed. This explains the crash when I try to do something with that view. However, I have no idea why -dealloc is being called on this view controller.
To dismiss the modal view controller, I have:
[self dismissModalViewController:YES];
As the code in an IBAction method in the modal view controller's UIViewController. This action is tied to a cancel button in the corresponding nib file.
In addition, my understanding from the View Controller Programming Guide is that it's OK to dismiss the modal controller from within itself, but it's more robust to use delegates. I was initially using a delegate, but took the delegate out to simplify debugging. I just put the delegate back in to double-check, and the same behavior occurs when using delegates. The modal controller's action method calls is implemented as:
[[self delegate] myModalViewController:self didAddObject:obj];
The delegate implementation in the parent view controller is:
[self dismissModalViewController:YES]
If anyone has seen this before or has any suggestions of what could be happening or how to debug this, I would greatly appreciate it.
If -dealloc is being called, something is releasing the view controller. Try implementing -release in your view controller:
-(void)release {
NSLog(#"view controller released");
[super release];
}
so that you can use the debugger to inspect the call stack when this unexpected release message happens.
Its dangerous to call dismissModalViewController from the modal view controller itself (message will be forwarded to parent view controller), if you have not retained it elsewhere. Normally, the parent view controller is responsible for dismissing the modal view controller it presented.
I'm working on an app that has three table view controllers in a navigation stack. The root view controller and the second VC have toolbars, but I want to add a subview to the second view controller like this. (The color is just there for visualization.)
I want to add the view programmatically, since I haven't been able to do it with IB without major headaches. Right now, I've been able to kind of get what I want by drawing a UIView in the second view controller like this:
- (void)viewDidLoad {
[super viewDidLoad]
UIView *detailView = [[UIView alloc] initWithFrame:CGRectMake(0, 392, 320, 44)];
detailView = [UIColor redColor];
[self.navigationController.view addSubview:detailView];
[detailView release];
}
The problem with this approach is that once the UIView is loaded in the second view controller, it stays loaded and is drawn in the third and root view controllers. I've tried a variety of methods of removing the UIView, including setting the detailView to nil in viewDidUnload, calling removeFromSuperview in didSelectRowAtIndexPath (which removed the view from the whole stack).
I've also tried adding the subview to self.view, but that pushes it below the visible area of the table view, so I have to scroll up to see it, and it snaps back down when I let go.
Clearly, adding this subview to the navigation controller is not the best way to do what I want, but I'm at a loss as to where to go from here.
As you've already discovered, you definitely should not be reaching up into the navigation controller's view.
You want your SecondViewController to be an UIViewController that implements the UITableViewDelegate and UITableViewDataSource and whose view lays out the UITableView and the UIView you wish to use for your stationary 'footer' in it's own main UIView.
It helps to keep in mind that UITableViewController is ultimately is just a convenience for creating a view controller whose view consists entirely of a UITableView.
Anyway, rather than attempt to put a pile of that code inline in this answer, you can browse it (or svn co) from this read-only svn repo.
EDITED (now that it's not midnight, putting some code/explanation directly in answer):
For the controller to be pushed onto the nav stack that needs the footer create a new UIViewController-based class (do NOT check the 'UITableViewController subclass' box in the template selection dialog).
Add instance variables for the UITableView and the UIView that is to be the extra bottom view.
#interface SecondViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView* tableView;
UIView* customFooterView;
}
#property (nonatomic,retain) IBOutlet UITableView* tableView;
#property (nonatomic,retain) IBOutlet UIView* customFooterView;
#end
In IB add a UITableView and UIView to the existing root view for the controller and lay them out as desired (probably worth altering the auto-resize parameters too if your app can be used in both landscape and portrait). Hook up the two views to the outlets defined for them in the "File's Owner" and also ensure you hook up the UITableView's delegate and dataSource properties to point at the "File's Owner."
Then just implement the UITableViewDelegate and UITableViewDataSource protocols as appropriate for your application.
If you want to lay out the entire 'footer' view in IB then go right ahead. Otherwise you can easily add items programmatically in viewDidLoad (and remember to tear it down in viewDidUnload).
I don't like the approach. You should put your table view inside another view, and put your detail view together in that view.
Despite of that, I think you can remove your view in viewWillDisappear method of your view controller. I also notice that you did not keep your detailView as a private variable, which you should do because you need to reference it when removing it later (I still wonder how you have done it.)
Note that viewDidUnload is called in case of view unloading (i.e. releasing from its controller), so it is not related to navigation.
Not sure which behavior you're looking for but try one of these:
Assign the detailView to the tableFooterView property of the tableview on the second VC.
Reduce the height of the table view and add the detailView to self.view.