4

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:

  1. ViewController class instantiate class BottomView and adds it as it's subview.
  2. Make a call on instance of BottomView class to start animate something.
  3. 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?

piotr_ch
  • 508
  • 3
  • 7
  • 17
  • 1
    check this http://stackoverflow.com/questions/1015608/how-to-perform-callbacks-in-objective-c – tkanzakic Apr 10 '13 at 13:41
  • Just clear you concepts about [delegate/delegation](http://developer.apple.com/library/ios/#documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html) – Inder Kumar Rathore Apr 10 '13 at 13:43
  • How do you start the animation off? – Paul.s Apr 10 '13 at 13:53
  • At the end of the animation in BottomView class I do [UIView setAnimationDidStopSelector:@selector(clearBottomPanel)]; But it doesn't solve my problem. Because I can't send message to the parent object that this animation is finished. – piotr_ch Apr 10 '13 at 13:58

5 Answers5

12

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];
Paul.s
  • 38,494
  • 5
  • 70
  • 88
6

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;

BergP
  • 3,453
  • 4
  • 33
  • 58
  • I was thinking about to do this with option 3. that you have mentioned. I have a problem with understanding without a simple example how it exactly works. How to define protocol and where it should be made? I need a simple example for my problem to get it understand well. I don't want to go for some kind of shortcut, but I really haven't found any good tutorial for me, so my question here ;) – piotr_ch Apr 10 '13 at 13:55
  • see my new edit (give me a minute) – BergP Apr 10 '13 at 13:59
  • @CrazyBucket, the question is edited. – BergP Apr 10 '13 at 14:24
  • 1
    For a simple case like this I would consider using blocks because the full `protocol` set up has the effect of shooting a shotgun of boilerplate code - bits go everywhere and the change isn't localised – Paul.s Apr 10 '13 at 14:29
  • @Paul.s, yes - blocks is better solution in this case – BergP Apr 10 '13 at 14:30
1

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
}];
Fonix
  • 11,447
  • 3
  • 45
  • 74
1

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.

guenis
  • 2,520
  • 2
  • 25
  • 37
0

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?

piotr_ch
  • 508
  • 3
  • 7
  • 17
  • try removing the view using for loop for ex: for (UIView *theView in [self.view subviews]){ [theView removeFromSuperview]}. It will remove all the subview. – ADJ Apr 17 '14 at 07:32