I'm wondering if I do it in right way. I have my class UIMyView which I add to my main UIView. In that UIMyView i do some stuff using NSTimer and after all i call removeFromSuperView method to back to main UIView. I would like to notice when my UIMyView was closed. I use NSNotificationCenter to do it but maybe there is some other better way to do that ?
EDIT:
OK but I think you you didn't understand me. Using setHidden or containsObject we need to do this action for example puttin it to click Button or something like that. What i wanna do is checking when UIMyView is closed without any user interactions.
try this one .may be this will helpfull for you
BOOL hasMapView = [self.contentView.subviews containsObject:self.mapView];
UIView *fromView, *toView;
if (hasMapView)
{
[btnMode setImage:[UIImage imageNamed:#"map-mode.png"] forState:UIControlStateNormal];
fromView = self.mapView;
toView = self.tblVideo;
[self.tblVideo reloadData];
}
else
{
fromView = self.tblVideo;
toView = self.mapView;
[btnMode setImage:[UIImage imageNamed:#"list-mode.png"] forState:UIControlStateNormal];
[self reloadPins];
}
use this instead of using removeFromSuperView
[urViewnm setHidden:YES];
To detect is View is closed
BOOL isViewActive = [self.view.subviews containsObject:UIMyView];
Using protocol you can achieve :
1.Create Protocol in CustomViewController.h
#protocol CustomViewControllerDelegate <NSObject>
- (void) didCustomViewControllerRemove:(id)sender;
#end
#interface CustomViewController : UIViewController
#property (retain) id <NSObject, CustomViewControllerDelegate> delegate;
#end
Implement Protocol in CustomViewController.m
#import "CustomViewController.h"
#implementation CustomViewController
- (IBAction)cancelBtnPressed:(id)sender {
[_delegate didCustomViewControllerRemove:self];
}
UIMyView.h
#import "CustomViewController.h"
#interface UIMyView : UIViewController '<'CustomViewControllerDelegate'>'
{ }
use delegate method UIMyView.m
- (void) addCustomViewControllerMethod {
CustomViewController *obj = [CustomViewController alloc]
initWithNibName:#"CustomViewController" bundle:nil];
[self.view addSubView: obj];
obj.delegate =self;
}
- (void)didCustomViewControllerRemove:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
Related
I have subclassed a view that I am using as header view it has some buttons delegate inside it and it works perfect .
However I am presenting a modalViewController above my viewController .
(in my modalViewController I have implemented the same header , and it does get the delegates from the header) but this view it self has to delegate to the previous viewController if the back button of the header is pressed.
I have made the same functions but my viewController never gets it's delegate... :(
I am quite new to Obj-C and I don't know maybe I am doing something illegal here.
here is the code of modalViewController trying to delegate to the previous viewController
#pragma mark - header delegate
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
//delete the items array
//_itemSourceArray = nil;
[delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
This delegate does triggers from the header view.
however in my previous viewController:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender //doesn't work :(
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
// [self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
never get called
I calling the modalViewController like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[_allEventsTableView deselectRowAtIndexPath:indexPath animated:YES];
[self->_allEventsTableView setNeedsDisplay];
[self->_allEventsTableView reloadData];
_drillPage = [[allEventsDrillPage alloc]initWithDictionary:((NSDictionary*) [_tableDataSource objectAtIndex:indexPath.row])];
_drillPage.delegate = self;
[self presentViewController:_drillPage animated:YES completion:nil];
}
in its .h file I did
#interface allEvents : UIViewController <headerDelegate , UITableViewDataSource , UITableViewDelegate ,allEventsDrillPageDelegate>
I don't get what am I missing here :-/ can some1 take a look please ?
if needed more info I will added just ask for it.
EDIT:
protocol of delegate inside the modalViewController
#class allEventsDrillPage;
#protocol allEventsDrillPageDelegate //define delegate protocol
- (void)allEventsDrillPage:(allEventsDrillPage*)allEventsDrillPage backbuttonPressed:(UIButton*)sender;
#end
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; //define
Let's try:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
// delete the items array
//_itemSourceArray = nil;
// my comment: you should replace "delegate" to "_delegate"
// and it works. I tested. It's OK. Wow.
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
nmh's answer is correct. I wrote up an answer earlier but his came in faster, so I thought I would take out the other parts and just add the explanations here.
You have this:
#interface allEventsDrillPage : UIViewController
{
id<allEventsDrillPageDelegate> __weak delegate;
....
}
#property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate;
With this id<allEventsDrillPageDelegate> __weak delegate; you declare an ivar.
With this #property (weak, nonatomic) id <allEventsDrillPageDelegate> delegate; you are declaring a property.
Since Xcode 4.4 you get auto-synthesization.
And so this line:
_drillPage.delegate = self;
You are setting the one via the property.
And not this:
id<allEventsDrillPageDelegate> __weak delegate;
So what you have here:
[delegate allEventsDrillPage:self backbuttonPressed:sender];
You are using the ivar above, not the one via the property.
And delegate is thus nil.
And so, if you try to send a message to the delegate using nmh's solution or:
[self.delegate allEventsDrillPage:self backbuttonPressed:sender];
It should work as expected.
Addendum to dismissing modal view controller:
Instead of dismissing it from the current view controller, dismiss it from the one who presented it, so:
- (void)header:(header *)header backbuttonPressed:(UIButton *)sender
{
if(header == logo)
{
NSLog(#"gotBackButtonDelegate");
[_delegate allEventsDrillPage:self backbuttonPressed:sender];
//or self.delegate
}
}
And in:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:nil];
_drillPage = nil;
}
Or even better:
- (void)allEventsDrillPage:(allEventsDrillPage *)allEventsDrillPage backbuttonPressed:(UIButton *)sender
{
NSLog(#"got back delegate!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
[self dismissViewControllerAnimated:YES completion:^{
_drillPage = nil;
}];
}
Hope this helps.
This question already has answers here:
How to perform Callbacks in Objective-C
(5 answers)
Closed 9 years ago.
I can't figure out how to make a function, that will give information to the parent object, that has done something, so I need your help.
Example what I want to make:
ViewController class instantiate class BottomView and adds it as it's subview.
Make a call on instance of BottomView class to start animate something.
After the animation ends, I want to give a sign that the animation has ended to the ViewController class, that it could release/remove an instance BottomView from itself.
I need something like a callback.
Could you help please?
You could do something like this
#interface BottomView : UIView
#property (nonatomic, copy) void (^onCompletion)(void);
- (void)start;
#end
#implementation BottomView
- (void)start
{
[UIView animateWithDuration:0.25f
animations:^{
// do some animation
} completion:^(BOOL finished){
if (self.onCompletion) {
self.onCompletion();
}
}];
}
#end
Which you would use like
BottomView *bottomView = [[BottomView alloc] initWithFrame:...];
bottomView.onCompletion = ^{
[bottomView removeFromSuperview];
NSLog(#"So something when animation is finished and view is remove");
};
[self.view addSubview:bottomView];
[bottomView start];
1.You can do it with blocks!
You can pass some block to BottomView.
2. Or you can do it with target-action.
you can pass to BottomView selector #selector(myMethod:) as action,
and pointer to the view controller as target. And after animation ends
use performeSelector: method.
3. Or you can define delegate #protocol and implement methods in your viewController,
and add delegate property in BottomView.
#property (assign) id delegate;
If you make some animations in your BottomView, you can use
UIView method
animateWithDuration:delay:options:animations:completion:
that uses blocks as callbacks
[UIView animateWithDuration:1.0f animations:^{
}];
update:
in ButtomView.h
#class BottomView;
#protocol BottomViewDelegate <NSObject>
- (void)bottomViewAnimationDone:(BottomView *) bottomView;
#end
#interface BottomView : UIView
#property (nonatomic, assign) id <BottomViewDelegate> delegate;
.....
#end
in ButtomView.m
- (void)notifyDelegateAboutAnimationDone {
if ([self.delegate respondsToSelector:#selector(bottomViewAnimationDone:)]) {
[self.delegate bottomViewAnimationDone:self];
}
}
and after animation complit you should call [self notifyDelegateAboutAnimationDone];
you should set you ViewController class to confirm to protocol BottomViewDelegate
in MyViewController.h
#interface MyViewController : UIViewController <BottomViewDelegate>
...
#end
and f.e. in viewDidLoad you should set bottomView.delegate = self;
if you specifically want to just run a function after an animation has occurred you could get away with just using this:
[UIView animateWithDuration:0.5 animations:^{
//do animations
} completion:^(BOOL finished){
//do something once completed
}];
Since you are using animations, a more specific way to handle it is to use animation delegates. You can set the delegate of your animation to the viewcontroller so that after the animation ends below method will be called in your viewcontroller.
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
To do that in the 2nd step you mentioned you should pass viewcontroller as an extra parameter so there in you can set the animation delegate.
Many thanks for all of you. I have tried two ways, by selector and by a delegate.
It's cool and I suppose, that I understand those mechanisms.
I have found one odd thing:
ViewController.h
#import <UIKit/UIKit.h>
#import "BottomPanelView.h"
#interface ViewController : UIViewController <BottomPanelViewDelegate>
#property (nonatomic, assign) BottomPanelView* bottomPanelView;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize bottomPanelView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
bottomPanelView = [[BottomPanelView alloc] initWithFrame:CGRectMake(0, 480, 320, 125)];
[bottomPanelView setDelegate:self];
[self.view addSubview: bottomPanelView];
[bottomPanelView start];
}
//delegate function
- (void)bottomViewAnimationDone:(BottomPanelView *)_bottomPanelView
{
NSLog(#"It Works!");
[bottomPanelView removeFromSuperview]; //1) Does it clears memory?
[bottomPanelView release]; //2) Strange is that if I have released it and set pointer to nil and use it below in touchesBegan it doesn't crashes. Why?
// but if I release it and don't set to nil, it will crash. Why reading from nil is okay and gives back 0 (myValue was set to 15)?
bottomPanelView = nil;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touched!");
NSLog(#"Pointer: %p", bottomPanelView);
NSUInteger val = [bottomPanelView myValue];
NSLog(#"myValue: %d", val);
}
My questions are in comments in code above.
Please, tell me especially why reading from nil pointer doesn't crashes the application?
I just started to learn obj-c and I have question about delegates. I know that on SOF is a lot of similar threads, but I was looking for and really didn't get my issue (maybe cause I'm beginner). Here's my problem: I want to use my own delegate and transfer an information from SlaveClass to MainClass. In SlaveClass in buttonDidClick: action, I declare delegate which is equal to NIL. Even I don't know where I should start to looking for mistake. Thanks in advance for any type of advice. Here's my code which refer to delegate:
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m (here appears NIL)
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}
MainClass.h
#interface MainClass : UIViewController <slaveDelegate>
#end
MainClass.m
-(void)transferNameDidClick:(NSString *)text
{
SlaveClass *delegate = [[SlaveClass alloc] init];
[delegate setMyOwnDelegate:self];
[_label setText:text];
NSLog(#"text: %#",text);
}
You are setting your delegate in wrong place. You have to set the delegate before going to slaveClass
mainClass.m
Present slave view controller like this
SlaveClass *slaveClass = [[SlaveClass alloc] init]
[slaveClass setMyOwnDelegate:self];
[self presentViewController:slaveClas animated:YES completion:nil];
-(void)transferNameDidClick:(NSString *)text
{
// This is the method getting called by the slaveClass. So it should know the delegate to call this.
[_label setText:text];
NSLog(#"text: %#",text);
}
Set you delegate out side the Custom delegate method. you are mistakenly setting inside the custom delegate method thats y delegate show nil. use like this.
Main Class .M
-(IBAction)nextView
{
nextView = [[ViewController2 alloc]init];
nextView.myOwnDelegate = self;
[self presentViewController:nextView animated:YES completion:nil];
}
-(void)transferNameDidClick:(NSString *)text;
{
NSLog(#"Value from Slave Delegate %#",text);
}
SlaveClass.h
#protocol slaveDelegate <NSObject>
-(void)transferNameDidClick:(NSString *)text;
#end
// --------------------------------------------------------------------------------
#interface SlaveClass : UIViewController
#property (nonatomic, strong) id <slaveDelegate> myOwnDelegate;
#end
SlaveClass.m
-(void)buttonDidClick:(id)sender
{
if ([_myOwnDelegate respondsToSelector:#selector(transferNameDidClick:)])
{
[_myOwnDelegate transferNameDidClick:(_textField.text)];
}
}
I am trying to make a custom protocol that i hope somebody might help with.
I have a mainViewController (mainVC) that has a label. That label needs to be updated with a string when i press a button in edwwVC.
I am using ARC and storyboard.
The problem is when i press the Done Button on the edwwVC, the "done" method is called BUT the delegate method is not called in mainVC.
Whereas, if i call the done method VIA the mainVC, then the done method is called AND the delegate method. So I can see the connection is there, I just do not understand why the delegate method is not called when i press the done button in the edwwVC.
I imagine it has something to do with the init of the edwwVC. Because it is already initiated by storyboard, so it looks to me as if I am initializing it again the in the viewDidLoad method of the mainVC. But that is how far i got :)
Thanks in advance!
edwwVC.h
#import <UIKit/UIKit.h>
#import "IIViewDeckController.h"
#class EDWWViewController;
#protocol EDWWViewControllerDelegate <NSObject>;
#optional
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC;
#end
#interface EDWWViewController : UIViewController <IIViewDeckControllerDelegate> {
__weak id<EDWWViewControllerDelegate> delegate;
NSMutableArray *edwwPoints;
}
#property (weak) id<EDWWViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) NSString *testString;
- (IBAction)done:(id)sender;
- (IBAction)add:(id)sender;
#end
edwwVC.m:
#pragma mark - delegate method
- (IBAction)done:(id)sender {
testString = #"This is the test string!";
[delegate edwwVCDidFinish:self];
[self.viewDeckController closeRightViewAnimated:YES];
NSLog(#"Done pressed");
}
MainVC.m:
- (void)viewDidLoad
{
[super viewDidLoad];
edwwViewController = [[EDWWViewController alloc] init];
edwwViewController.delegate = self;
}
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC {
edwwLabel.text= edwwVC.testString;
NSLog(#"delegate method called");
}
Remove the line ...
__weak id<EDWWViewControllerDelegate> delegate;
From the .h and change the line...
[delegate edwwVCDidFinish:self];
to...
[self.delegate edwwVCDidFinish:self];
In the .m.
That should sort it.
The way you have it set up the ivar delegate is not the same as the property delegate (which is actually an ivar called _delegate) (thanks #Joris Kluivers, just adding for clarity). They are pointing to different things.
If you add a breakpoint where you are calling the delegate method I think you'll find that delegate is nil. Whereas _delegate (or self.delegate) is not nil.
::EDIT::
Ahh... just spotted the second bit too.
If you are setting up the edwwvc in storyboard then you should be alloc initing it too.
If you are segue-ing to the edwwvc then you should intercept the segue in mainVC.m like this...
- (void)prepareForSegue: //blah the rest of the name...
{
if ([segue.identifier isEqualToString:#"the name of your segue"])
{
EDWWViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
}
This will take the controller that you are pushing to from the storyboard and set the delegate to it.
:: ANOTHER EDIT ::
If EDWWVC is inside a containerViewController then you can do this inside viewDidLoad in MainVC.m...
- (void)viewDidLoad
{
// other stuff...
for (UIViewController *controller in self.childViewControllers) {
if ([controller isKindOfClass:[EDWWViewController class]]) {
EDWWViewController *edwwvc = (EDWWViewController*)controller;
eddwvc.delegate = self;
}
}
}
You may find this code has to go in viewDidAppear or something but I think viewDidLoad shouldd work just fine.
You may actually be able to set the delegate property directly by using the storyboard to (but I'm not 100% certain on this).
The answer was in the containerVC of both controllers.
Where i initialized the view controllers: the viewDidLoad of the containerVC m file:
- (void)viewDidLoad
{
[super viewDidLoad];
mainVC = (MainViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"MainVC"];
edwwVC = (EDWWViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"EDWWVC"];
//THIS LINE WAS MISSING
edwwVC.delegate = mainVC;
self.centerController = mainVC;
self.rightController = edwwVC;
}
BUT guys thanks for the help! :) Appreciate it got me in the right direction! :) THANKS! :)
I am having trouble calling the the popoverControllerDidDismissPopover method in as much as I do not know where to put it and how to call it.
I have created a popover as follows -
// SettingsViewController.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "ViewController.h"
#import "SharedData.h"
#import "PlayerPopUpVC.h"
#interface SettingsViewController : UIViewController <UITableViewDataSource, UIPopoverControllerDelegate> {
- (IBAction)popUp:(id)sender;
#property (strong, nonatomic) UIPopoverController *playerPopUpVC;
#property (strong, nonatomic) PlayerPopUpVC *popUp;
// SettingsViewController.m
#import "SettingsViewController.h"
- (IBAction)popUp:(id)sender {
UIButton *editPlayers = (UIButton *)sender;
if(self.playerPopUpVC) {
self.popUp= [[PlayerPopUpVC alloc] initWithNibName:#"PlayerPopUpVC" bundle:nil];
self.popUp=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
}
[self.playerPopUpVC presentPopoverFromRect:[editPlayers frame] inView:[editPlayers superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
I know I have to set the delegate of my PopOver to self in order to call the method but cannot work out what the code is.
I have tried -
self.playerPopUpVC.delegate=self
but Xcode does not like it.
My popOver class looks like this -
// PlayerPopUpVC.h
#import <UIKit/UIKit.h>
#interface PlayerPopUpVC : UIViewController <UITableViewDataSource, UIPopoverControllerDelegate> {
}
// PlayerPopUpVC.m
#import "PlayerPopUpVC.h"
#interface PlayerPopUpVC ()
#end
- (void)viewDidLoad
{
[super viewDidLoad];
self.modalInPopover = NO;
self.contentSizeForViewInPopover = CGSizeMake(240, 400);
}
Any help would be most welcome. I have spent a week now trying to sort it.
First, you need to understand the delegate pattern, which seems that you dont fully understand yet.
The popover will be the one which will call the popoverControllerDidDismissPopover method on the delegate. You only have to implement the UIPopoverControllerDelegate protocol in your class and assign yourself as the delegate of the popover. Why do you say that XCode doesn't like it? please, provide more info.
Furthermore, you are making an incorrect assignment here:
self.popUp=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
Edit: Provided more code to help with the error. Please, review the delegate pattern next time before making these questions.
Your SettingsController.m should have this instead:
- (IBAction)popUp:(id)sender {
UIButton *editPlayers = (UIButton *)sender;
if(!self.popUp) {
self.popUp= [[PlayerPopUpVC alloc] initWithNibName:#"PlayerPopUpVC" bundle:nil];
}
self.playerPopUpVC=[[UIPopoverController alloc] initWithContentViewController:self.popUp];
self.playerPopUpVC.delegate = self;
[self.playerPopUpVC presentPopoverFromRect:[editPlayers frame] inView:[editPlayers superview] permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
// Your code here
}