Calling a function of UIViewController from a child UIView - iphone

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...

Related

Returning value to parent view

I've got the following situation: I've got a UISwipeGestureRecognizer hooked up on my table cell. On swipe, two buttons get their .hidden-status switched to NO.
Those cells are custom made. Meaning, the IBAction of those buttons is in that controller. Now I'd like the UIButton in those custom cells to send back a variable to the parent view. That parent view is the UIViewController (containing a UITableView).
How can I do this? I've read about protocols, but I can't get a solid example for that... Or are there any other ideas? They are all welcome :)
The approach you are following may result in a crash or a memory leak as you have to retain the controller of your uitableview cell content.
However protocols are simple approach where after you create your table view cell you can allocate the viewcontroller (which creates the table view) as a delegate to all actions for the cell's content view.
In your custom cell's controller just call the delegate method and it will be handled by your parent view.
It may sound confusing, but its really simple. Will try to post sample code.
#protocol yourDelegate;
#interface yourViewController{
id<yourDelegate> delegate;
}
#property (nonatomic,assign) id<yourDelegate> delegate;
#end
#protocol yourDelegate
- (void)passThisActionToMainController;
- (void)passThisEventToMainController;
#end
Once you are done with the above in your table view cell view controller, in your main controller create an instance of your view controller and then
controller.delegate = self;
In your cell's view controller when a user taps or performs any action then call
[delegate passThisActionToMainController];
or
[delegate passThisEventToMainController];
This way your main controller gets the event and it can be processed.
Alternate way to do this would be to create the buttons when you create the cell and handle the events. I would recommend this approach.
[myButton addTarget:self action:#selector(whatMyButtonShouldDo:)
forControlEvents:UIControlEventTouchUpInside];
The above line is copied from an answer here.

Adding UIView subview to single table view controller in navigation stack

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.

How to controll second UIView (subview) from a different UIViewController file in one XIB

I am strugling on the following task.
I am trying to control my subview from another viewController class.
What I did and does not work is this.
I inserted an object and changed it class to my second viewController class.
Then I connected its UIButton outlet to a button I have on my subview.
I then connected the buttons action to the outlet of my second view controller.
What I get when I run is this.
It all shows up well but when I try to touch the button that resides in my subview app crashes. I am only left with a worringing: "Action unavailable: The "Touch Up Inside" event of "Rounded Rect Button".
It's probably my logic that is incorrect. Thanks for help.
Well after a long research I got an answer to my problem.
As it appears I was doing everything right.
The problem is that after the initial XIB file is initialized it autoreleases all views and subviews. So to prevent from gettig your second view controller from being released implement this method
- (void)awakeFromNib {
[self retain];
}
in your view controller .m file.
This method will retain your second view controller alive and allow it to recive and respond to UI actions.

superview and parentviewcontroller nil after adding a subview

I think I'm missing something fundamental and so I want to ask the community for some help. I'm building an app based around a basic iPhone Utility Application. My MainView and FlipsideView share some elements so I have created separate ViewControllers and nib files for those pieces. In order to do this I have done the following:
1. Created a viewcontroller called searchDateViewController which is the file's owner of searchDateView.xib
2. searchDateView.xib is basically a UIView with a UILabel inside, the view is wired up correctly
3. Inside both MainViewController.m and FlipsideViewController.m I add a subview as folllows:
- (void)loadView{
[super loadView];
searchDateViewController = [[SearchDateViewController alloc] initWithNibName:#"SearchDateView" bundle:nil];
[[searchDateViewController view] setFrame:[searchDateView frame]];
[[self view] addSubview:[searchDateViewController view]];
...
}
Everything displays and works just fine. Basically depending on actions that happen in each of the main and flipside views the UILabel of the nib is changed. However, I wanted to do something slightly different if the searchDateViewController is loaded from the MainView or the FlipsideView. However, I can't seem to figure out which ViewController is adding the searchDateViewController subview.
In searchDateViewController I tried:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"superview %#", self.view.superview);
NSLog(#"parentviewcontroller %#", self.parentViewController);
}
In both cases I get nil.
So my question is - can I find out which ViewController is adding searchDateViewController a a subview? If so how? Or if my logic here is completely messed up, how should I be doing this?
Thanks!
viewDidLoad is invoked when the view controller has loaded its view. In your case, that happends in this line:
[[searchDateViewController view] setFrame:[searchDateView frame]];
At that moment, you haven't yet called addSubview: so it is no wonder the view's superview is nil.
To solve your problem, you should define a property inside SearchDateViewController to distinguish between the different cases. This property would then be set accordingly by the parent controller that creates the SearchDateViewController instance.
Generally, I do not think it is a good idea to use a UIViewController subclass as a controller for a view that is used as a subview of one or several fullscreen views rather than be used as a fullscreen view itself. Much of UIViewController's logic works on the assumption that it is used to manage a fullscreen view. For instance, with your design, I think it's possible that SearchDateViewController will modify the view's frame when the device orientation changes etc. Since you don't need all this functionality for a non-fullscreen subview, I suggest you subclass your SearchDateViewController directly from NSObject.
ViewController and views are completely separate.
In most cases, when you add a subview to a parent view you don't add its controller to the parent's viewController. The exception to this rule is the navigation controller which adds the controller instead of the view to maintain a hierarchy of view controllers.
Your SearchDate viewController can't find a parent controller because you never assigned one and the system does not do it automatically. You can just assign a parent controller when you evoke the view from another controller.
searchDateViewController.parentController=self;

How to scroll to tableview row?

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.