I have a UIView class which I am currently removing from my view by using from inside the class [self removeFromSuperview]. Hopefully that's the correct thing to do.
However, now from my view controller (of which I add this view to) I need to know when it has removed itself so that I can call a method when this happens.
Generally speaking, the view shouldn't be doing things like removing itself. That's the job of the view controller.
If a UIView subclass can produce events that require the view hierarchy to be changed, I would define a delegate property for that view, and when an event occurs, call a method on that delegate. Then, when your view controller adds the view, it would set itself as the delegate and define the relevant method to handle the event.
If you have removed the UIView
view.removeFromSuperview()
you can check if it exists with the following code:
if !(view.superview != nil) {
//no superview means not in the view hierarchy
}
You could have a delegate callback setting the controller as the view's delegate. When you're about to remove the view, make the delegate callback and implement the callback method in your controller.
The 'removeFromSuperview' has always seemed backwards to me… :(
I'm assuming you are making the remove call after some sort of action, like a button press or something. if that is the case, set the buttons delegate to be the view controller, not the view class, and inside the action method in the view controller, call
[yourCustomView removeFromSuperview];
The best choice would be to let the controller remove the view
[self.view removeFromSuperview];
and to know if the view was removed (or never added) you can ask
if(![self.view superview]) {
//no superview means not in the view hierarchy
}
Not sure what sdk you are using - but I am using iOS 5 and I just use the following method in the superview:
-(void)willRemoveSubview:(UIView *)subview{
if([subview isEqual:someView]){
//do stuff
}
//you could do some stuff here too
}
Related
I have a app out for testing right now that's almost completely done - just a few bug fixes left. Unfortunately, the customer decided that they'd like the entire main page of the app to be inside of a scroll view (so that there's more room for the table at the bottom). I already have everything set up and I don't really want to move everything and change references. Is there an easy way to change the class of the main view to a scroll view? I've already tried changing the class in IB and setting the class type in the init method. If there isn't I'll probably just throw the top section of the view into a nib file and load it as a custom cell.
-EDIT- I ended up changing the class type in IB and then doing
[(UIScrollView *) self.view setScrollEnabled:YES];
[(UIScrollView *) self.view setContentSize:CGSizeMake(0,2000)];
in viewDidLoad. Thanks for the help, wish I could accept all your answers.
When you are referring to [self view], I am going to assume you mean in a view controller. The view of a view controller can be any view that derives from UIView. Thus a scrollview is completely acceptable.
I don't really want to move everything and change references.
what would you have to move? why would you have to change references? Only thing you should need to do is add a scroll view to your view controller, set the view controllers view to it, and add the current view as a subview to the new scroll view. No references need to be changed, nothing has to be moved.
Refer to loadView method in documentation of view controller.
Here is a simple (untested!) example
- (void)loadView {
UIScrollView *scrollView = [[UIScrollView alloc] init] autorelease];
//Set the properties of scrollview appropriately....
self.view = scrollView;
}
Now the root view of your view controller will be a scroll view.
Note
- As the documentation states, do not do this if you are using interface builder to initialize your views/view controller. I could not tell from your description if this was the case or not. If it is, you should be able to change the view type in interface builder.
You need to set the contentSize property of your scrollview.
Since you are using IB, the easiest way to do this is to put all your UI elements into a view and add this single view to your scroll view. In the viewDidLoad method, set the content size of the scrollview to be the same size as the view that contains all your UI.
As an aside, there are much easier ways to reference views than walking down the view hierarchy, as you seem to be doing. viewcontroller.view.something.tableview. Add a connection to the tableview from your view controller in IB and it doesn't matter where that tableview is in the view hierarchy. You'll always be able to reach it from viewcontroller.tableview, no matter how you rearrange your nibs.
I think you have to use a pointer with proper type. Example for Google Maps: let's say you changed you base view's class to GMSMapView.
MapViewController.h
#property GMSMapView *mapView;
MapViewController.m
-(void)awakeFromNib{
[super awakeFromNib];
self.mapView = (GMSMapView*)self.view;
// ... etc.
}
-(void)Complete{
CM(#"complete");
[BNUtilitiesQuick UtilitiesQuick].startForm=0;
[self.view removeFromSuperview]; //This remove superView and the new view will be listNewController
[self setSearchLocationWhenChangeSearchBar];
if([self.searchListTemp isEqualToString:[cachedProperties singleton].searchList] && [self.searchLocationTemp isEqualToString:[cachedProperties singleton].searchLocation]){
CLog(#"Don't do anything");
}
else{
...
//Blablabla
//[Timer searchCriteriaChanged];
}
[[BNUtilitiesQuick ListController] viewWillAppear:true];//It's not called if I don't do this
}
Part of me feel that it should be called automatically. Somehow I must have done something wrong when it's not called.
Obviously I don't want things to get called twice either.
So I ended up calling them explicitly. Somehow it feels wrong. Am I wrong?
viewWillAppear is declared in UIViewController.h , it gets called automatically every time the view is about to appear. at some point you have to use super in your implementation.that could be the reason why its not called automatically, because your BNUtilitiesQuick is not a class of UIViewController
If you want some action to happen just before your view disappears, you can write your code in the viewWillDisappear method. This method notifies the view controller that its view is about to be removed from a view hierarchy.
- (void)viewWillDisappear:(BOOL)animated{}
please explain me, why I need to use addChildViewController: method?
After all, when I put subview to view [some_obj.view addSubview:some_view], these subview knows about his controller.
Thanks.
addChildViewController
Needs to be called, cause the parent controller needs to hold a reference to its child view controller for lifetime / background management... this reference cannot / shouldn't be obtained from UIView, when added as a subview
Also you can than reference parent from the child via self.parentViewController
Apple Says about addChildViewController
Adds the given view controller as a child. If the new child view
controller is already the child of a container view controller, it is
removed from that container before being added. This method is only
intended to be called by an implementation of a custom container view
controller. If you override this method, you must call super in your
implementation.
For adding / removing, you can refer to this great category and have no worry when to call it:
UIViewController + Container
- (void)containerAddChildViewController:(UIViewController *)childViewController {
[self addChildViewController:childViewController];
[self.view addSubview:childViewController.view];
[childViewController didMoveToParentViewController:self];
}
- (void)containerRemoveChildViewController:(UIViewController *)childViewController {
[childViewController willMoveToParentViewController:nil];
[childViewController.view removeFromSuperview];
[childViewController removeFromParentViewController];
}
I have a UIButton that I create in my sub class ViewController, and add it to my MainViewController.
Now, I added a target method to this button that should push another view controller to my Navigation controller (the one that in the MainViewController).
I know that the method did call when I push the button, but the view wasn't push to the Navigation Controller.
I scanned this drawing - this is the drawing (I also added part of my code):
This is the code I'm using in my button:
(remember it's in a deferent ViewController).
- (void)buttonPressed:(UIButton *)sender
{
Photo_ScreenGlobalView *photo = [[Photo_ScreenGlobalView alloc] init];
[self.navigationController pushViewController:photo animated:YES];
}
Usually I solve these situations with delegation. If there is a view controller which is subordinate to another (i.e. a "sub" view controller) but should have the ability to trigger navigation changes, etc... then it should have a weak pointer back to it's 'parent'. Then the parent VC should implement an appropriately named protocol with a callback for the child to use. The names of these things can be generic, such as #property navigationDelegate and requestNavigationToViewController: or they can be more semantic, such as #property userFormDelegate and userFormDoneButtonPressed:
Generally speaking, a subordinate view controller should not be able to directly modify navigation at it's parent's level; but it can trigger it via more loosely-coupoled interfaces like these.
i came back to let you all know how i actually did it.
after googling a lot found this nice and quick guide how to make DELEGATE
and working with delegate solved all my problems. if you need any help don't hesitate to send me PM.
this is the guide:
http://css.dzone.com/articles/do-not-publishcreating-your
i have two UIViewControllers for example A and B.
On A there is a button when pushed loads view B as a sub view. Using the standard initWithNibName. On B there is a button which dismisses View B and should call a method in view A (its Parent View). I thought it should be a simple [super methoddName:perimeter] but this doesn't work. after placing a NSLog printing out the name of the parentViewController it returned null which is odd as surly it should return View A as the Parent view.
any Ideas Cheers
super refers to your class's superclass, not its parent view controller.
A way to quickly do this is create a new init method that takes an instance of view controller A, use that to set the target of your button.
The best way is to define a new protocol on view controller B. Make view controller A conform to it. On init of B, pass in a refernce to A to use as your delegate. Then on the button push in B call the desired method on the delegate.
You might be better off by letting view controller A be a delegate on view controller B.
A can set the delegate property on B when after loading it but before pushing it into view, and when B needs to call the required method, it can just call it on the delegate object.
That makes the coupling between the controllers looser, and if you let the delegate conform to a protocol, you can keep B's knowledge of A to a minimum.
It's better design, if you ask me.
parentViewController won't work if you add view controller B's view as a subview. You will have to find the view controller A in a different way.
Say, if you had added B's view as a subview like this,
[A.view addSubview:B.view];
then you can call view controller A like this.
[[B.view.superview nextResponder] methoddName:perimeter];
If you have added B.view as a subview somewhere within the view hierarchy of A.view.
UIResponder * responder = B.view.superview;
do {
responder = [responder nextResponder];
} while ( ![responder isMemberOfClass:[A class]] );
// `responder` is referencing your view controller A. You will have to cast it.
Easiest way to do this especially if it is a static method:
ViewControllerB
#import "ViewControllerA.h"
- (void)dismissView{
//implementation to close the view
[ViewControllerA methodName:perimeter];
}
Good Luck!