How to notify superview of subview removal - ios

I added a custom UIView to a UIViewController and after some code in the view, I want to remove this view from the UIViewController, but I am not sure how to notify the UIViewController of the UIView's removal.
I am using this method to exit from within the UIView
-(void)exit{
[self removeFromSuperview];
}
Do I need to set a listener? Any help is appreciated
I posted a detailed solution. Thanks Rage, Bill L, and FreeNickname

Since it is not convenient to write a code as a comment, I'll write it as an answer. This answer illustrates what #Rage suggested in his answer.
First, you create a #protocol for your CustomView and add a delegate for it. You declare that the delegate should conform to this protocol. Then in your ViewController you implement your protocol and set ViewController as a delegate of your CustomView.
Like so:
CustomView.h:
#protocol CustomViewDelegate<NSObject>
//You can also declare it as #optional. In this case your delegate can
//ignore this method. And when you call it, you have to check, whether
//your delegate implements it or not.
-(void)viewWasRemoved:(UIView *)view
#end
#interface CustomView: UIView
#property (nonatomic, weak) id<CustomViewDelegate> delegate;
#end
CustomView.m:
#implementation CustomView
-(void)exit {
[self removeFromSuperview];
//if your method is NOT #optional:
[self.delegate viewWasRemoved:self];
//if it IS #optional:
if ([self.delegate respondsToSelector:#selector(viewWasRemoved:)]) {
[self.delegate viewWasRemoved:self];
}
}
#end
ViewController.m:
#import "CustomView.h"
#interface ViewController()<CustomViewDelegate>
#end
#implementation ViewController
-(void)someMethod {
self.customView.delegate = self;
}
-(void)viewWasRemoved:(UIView *)view {
//Whatever
}
#end

Use delegation. Add a protocol to your custom View which implements a method to notify the removal of the subview.
Make the View controller the delegate while adding the custom view. In your custom class call the delegate method right before [self removeFromSuperview];

Set up a delegate for it, with a method called viewWasRemoved: or something similar. Set your view's delegate to be the ViewController you want to notify, and then in your exit method, call [self.delegate viewWasRemoved:self];, which will then kick off the viewWasRemoved: method in your ViewController, where you can do any relevant work you need to do once the view is removed.

I'll post a detailed solution in case it helps anyone out, or if anyone can offer any pointers. Thanks Rage, Bill L, and FreeNickname:
The answer is to use delegation
First I imported the superview to the .h of my subview:
#import "ViewController.h"
Then I add a delegate id to the same file:
#property (weak) id delegate;
Then when initializing the custom UIView in the superview, I set the delegate:
CustomView *view = [[CustomView alloc]initWithFrame:frame];
view.delegate = self;
I add this method in the superview for a callback:
- (void) viewWasRemoved: (UIView *) view{
NSLog(#"removed");
}
Then finally call the method in my subView:
-(void)exit{
[self.delegate viewWasRemoved:self];
[self removeFromSuperview];
}

Related

Detect when UIView is added in other UIView which MUST has a UIViewController?

Is there a notification or callback when a UIView is added in other UIView which has a UIViewController. We can get a UIView's ViewController, if it has one, via nextResponder. (Reference)
But depending on nextResponder is not reliable, if the UIView is not been added into a View which has ViewController, this method fails. For example, when we are calling from the cell's responder in UITableViewDataSource's cellForRowAtIndexPath. Because by the time of calling cellForRowAtIndexPath, the cell being dequeued hasn't been added into UITableView yet. However, we can call that method in - tableView:willDisplayCell:forRowAtIndexPath:, because by the time of calling - tableView:willDisplayCell:forRowAtIndexPath:, the cell is already being added into TableView.
So if you have ViewA which is the view of ViewControllerA and you want to add the ViewB you can subclass ViewB
#protocol ViewBDelegate
- (void) viewAddedToSuperview:(ViewB*)sender;
#end
#interface ViewB: UIView
#property (nonatomic, assign) id< ViewBDelegate > delegate;
#end
#implementation
- (void)didMoveToSuperview {
[super didMoveToSuperview];
[delegate viewAddedToSuperview:self];
}
#end
and in ViewControllerA you implements the protocol ViewBDelegate.
This is just an example of my idea.
Let me know in the comments it is can help you, otherwise I will try to propose other solutions depending on your goals.

How to call performSegueWithIdentifier from within a custom class of a view that's a subview of view controller?

I have a view controller that has a subview that uses a custom class. When I perform performSegueWithIdentifier from the context of the view controller, it works fine, however, how can I call performSegueWithIdentifier from within the context of child subview?
You can't call performSegue from an UIView. You can use a protocol, make the viewController implement it and set it as delegate for your custom view.
CustomView.h
#protocol CustomViewDelegate
-(void)customView:(CustomView *)view performSegueWithIdentifier:(NSString *)identifier;
#end
#property (nonatomic, weak) id<CustomViewDelegate> delegate;
CustomView.m
// This is the event on which you would like to perform the segue
-(void)didClickButton {
[self.delegate customView:self performSegueWithIdentifier:#"mySegue"];
}
ViewController.m
-(void)viewDidLoad {
// All you other stuff...
// Set the delegate
self.customView.delegate = self;
}
-(void)customView:(CustomView *)view performSegueWithIdentifier:(NSString *)identifier {
[self performSegueWithIdentifier:identifier];
}
Hope this helps. Let me know if you have questions

ios: delegate doesn't respond

i'm trying to create my first delegate here's what i'm trying to do
at class called PAStepper.h i did this
#class PAStepper;
#protocol StepperDelegate <NSObject>
#required
-(void)didIncrement;
-(void)didDecrement;
#end
#interface PAStepper : UIControl
{
__weak id <StepperDelegate> stepperDelegate;
}
#property (nonatomic, weak) id <StepperDelegate> stepperDelegate;
#end
and synthized the property of course in the .m file
and then putten in the PAStepper.m the following in a method
[self.stepperDelegate didDecrement];
in the controller i want to take the delegate i did this
.h
#interface OCSideCartViewController : UIViewController<StepperDelegate>
.m
- (void)viewDidLoad
{
[super viewDidLoad];
PAStepper *stepper = [[PAStepper alloc]init];
stepper.stepperDelegate = self;
}
-(void)didIncrement{
NSLog(#"inc");
}
-(void)didDecrement{
NSLog(#"dec");
}
knowing that this class is viewController that contain a table that every cell have PAStepper .. all i wanted that the delegate call didIncrement and didDecrement whenever i press the buttons
When you call this method
[self.stepperDelegate didDecrement];
NSLog you stepperDelegate and ensure it is not null. Can you confirm the method which calls your delegate is being called?
you can call this in your pastapper.h file
if ([stepperDelegate respondsToSelector:#selector(didIncrement)])
{
[stepperDelegate performSelector:#selector(didIncrement)];
}
I can´t download the project, so i am guessing..
Is the Stepper in the Interface Builder?
If yes you don´t have to allocate it.
Make an Outlet Connection of the Stepper in the OCSideCartViewController.
Post this in OCSideCartViewController:
- (void)viewWillAppear:(BOOL)animated{
self.stepper.stepperDelegate = self;
[super viewWillAppear:animated];
}
If the Stepper is not an Outlet, you have to add it to your ViewControllers view after setting it´s bounds..

How to create a class with a UIViewController when the subclass of UIViewController is unknown

The title is what I think I need but i will go back one step. I want to create a class which handles certain things in an iOS app. This class might be called by multiple UIViewcontrollers in an iOS app. The class may need to show a UIView at some stage for user input. So my question is how can I show a UIView when I don't know which subclass of UIViewController is calling it? To what can I add the UIView from this class?
I suppose there are two possible answers either the class finds the current UIViewController or the calling subclass of UIViewController passes itself to the class so the class knows.
How is this supposed to be done.
Thanks guys for your help.
I'm going to expand on #ericleaf's comment regarding using a protocol and subclasses. It sounds like you are asking the following:
How can I create a resusable, generic class that presents a view
within a UIViewController subclass?
A great way to do this is to define a protocol in your generic class and have your view controller subclasses support this protocol. The protocol defines an interface for your custom class to comunicate with it's delegate, in this case a UIViewController subclass. Other than the protocol, the objects don't need to know anything else about the implementation of each other.
Any information your custom object needs to be able to present views within it's delegate would be passed via protocol methods. The specifics of the protocol are up to you based on your needs. You could have the custom object "ask" the delegate for information (e.g. what view should I put a subview in?) or you could have the protocol provide information to the delegate and let the delegate deal with it (e.g. here is a subview you can put wherever you want).
There is a lot of great documentation on protocols available on SO and elsewhere. This is long enough already so I kept the example fairly simple.
custom class .h file with protocol definition
// my custom class that adds adds a view to a view controller that supports it's protocol
// forward class definition for the protocol
#class MyAwesomeObject;
#protocol MyAweseomeObjectDelegate <NSObject>
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject;
#end
// this could be defined such that the delegate *must* be a UIViewController. I've left it generic.
#interface MyAwesomeClassObject : NSObject
#property (nonatomic, weak) id <MyAwesomeObjectDelegate> delegate;
#end
custom class .m file
// MyAwesomeObject.m
#import "MyAwesomeObject.h"
#implementation MyAwesomeObject
// this is a dumb example, but shows how to get the view from the delegate
// and add a subview to it
- (void)presentViewInDelegate
{
UIView *containingView = [self.delegate viewForMyAwesomeObject:self];
if (containingView) {
UIView *subview = [[UIView alloc] initWithFrame:containingView.bounds];
subview.backgroundColor = [UIColor redColor];
subview.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
[containingView addSubview:subview];
}
}
MyViewController .h using the custom object
// MyViewController.h
#import "MyAwesomeObject.h"
#interface MyViewController : UIViewController <MyAwesomeObjectDelegate>
#property (nonatomic, strong) MyAwesomeObject *awesomeObject;
#end
MyViewController .m using the custom object
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)init
{
self = [super init];
if (self) {
_awesomeObject = [[MyAwesomeObject alloc] init];
_awesomeObject.delegate = self;
}
return self;
}
// MyAwesomeObjectDelegate
- (UIView *)viewForMyAwesomeObject:(MyAwesomeObject *)awesomeObject
{
return self.view;
}
You can get the class into a string and do a compare.
For example, lets assume your custom UIViewController subclass is CustomViewCon and the UIViewController object reference is myUnknownClassObject, then:
NSString *classString = NSStringFromClass([myUnknownClassObject class]);
Then you can:
if([classString isEqualToString:#"CustomViewCon"]){
//do something like maybe present a particular view
myUnknownClassObject.view = myCustomView; //or anything..
}
Similarly you can check for any class.
Edit: According to the suggestions from comments, you could also do the following(better way):
if([[myUnknownClassObject class] isKindOfClass:[CustomViewCon class]]){
//same as before
}
Why wont you use a block for this?
BaseViewController.h:
#property (copy) void (^addViewBlock)();
- (IBAction)showViewWhenNeeded;
BaseViewController.m:
- (IBAction)showViewWhenNeeded
{
if (self.addViewBlock)
self.addViewBlock();
}
And in your child class, set that block's actions, and call the method when you feel like you should put up a view.
ChildViewController.m
// within some method, propably init or smth
[self setAddViewBlock:^{
[self.vied addSubView:...];
}];
// when need to actually add the view
[self showViewWhenNeeded];

Accessing UIViewcontroller from a subview - iOS

I have a UIView .xib file. This is opened from the storyboard entry point UIViewController1 as a subview. The subview has a IBButton which when pressed opens a UIViewController2. Is this possible by any chance?
First, create a segue going from your first view controller to your second. I'm going to name it OpenViewController2. We'll be able to call that segue programmatically.
In your UIView .h file, create a protocol that defines a delegate for that view:
SomethingView.h:
#protocol SomethingViewDelegate <NSObject> {
- (void)importantThingHappened;
}
...
#interface SomethingView {
id<SomethingViewDelegate> delegate;
}
#property (nonatomic, strong) id<SomethingViewDelegate> delegate;
SomethingView.m:
#implementation
#synthesize delegate;
...
// The IBAction for the button in your view
- (IBAction)buttonClicked:(id)sender {
[delegate importantThingHappened];
}
MyViewController.m, where you create your view:
// Create view
UIView *myView = ...
myView.delegate = self;
Later in MyViewController:
// Implement the protocol. This function will be called when the button action
// inside of the UIView you created is pressed
- (void)importantThingHappened {
[self performSegueWithIdentifier:#"OpenViewController2" sender:self];
}
First give your IBButton an unique tag, and in your UIViewController's viewDidLoad,
// add following line into viewDidLoad
[(UIButton*)[self.view viewWithTag:MY_UNIQUE_TAG_FOR_BUTTON] addTarget:self action:#selector(buttonPressed:) forControlEvent:UIControlEventTouchUpInside];
and finally implement the buttonPressed: for whatever you want
-(void) buttonPressed:(UIButton*)aButton {
// do what you want to do.
}

Resources