0

New to iOS development & Objective-C and am a little unsure how to go about solving this issue.

I'm working on a project that works like a video player. There are two ViewControllers:

  • MenuViewController (has a list of titles that act as buttons)
  • PlayerViewController (view where video plays)

In the MenuViewController, I want to be able to click onto a button (video title) :

- (IBAction)videoOne:(id)sender
{
    PlayerViewController * vc = [[PlayerViewController alloc] initWithNibName:@"PlayerViewController" bundle:nil];

    [self presentModalViewController:vc animated:YES];
}

and have an action that's currently defined in the PlayerViewController automatically execute as soon as its loaded.

Is there a way to have a button in one ViewController call the action in another ViewController as soon as that second ViewController has loaded?

Thanks!

dace
  • 5,819
  • 13
  • 44
  • 74
  • Use Delegates to achieve what you need. Or pass a value/flag to another view controller, and when u load that view controller check for that value and load the necessary methods. – Teja Nandamuri Jan 07 '16 at 19:05
  • You could try passing a block. Seems like PlayerViewController knows the "when" (finished loading) and MenuViewController knows the "how" (what should be done). So you would pass a block (the "how") from MenuViewController to the PlayerViewController which would then execute that block at the appropriate time (the "when"). – CrimsonChris Jan 07 '16 at 19:13

3 Answers3

2

The right way to solve this problem will be set up a delegate pattern between the two view-controllers.

Example:

@protocol PlayerViewControllerDelegate<NSObject>
{
    -(void)playerViewControllerViewDidLoad:(PlayerViewController *)playerVC;
}

Then, in PlayerViewController.h create a weak delegate variable:

@property (nonatomic, weak) id<PlayerViewControllerDelegate> delegate;

In PlayerViewController.m, notify the delegate on viewDidLoad:

-(void)viewDidLoad {

    [super viewDidLoad];

    [self.delegate playerViewControllerViewDidLoad:self]

}

In MenuViewController:

- (IBAction)videoOne:(id)sender
{
    PlayerViewController * vc = [[PlayerViewController alloc] initWithNibName:@"PlayerViewController" bundle:nil];
    vc.delegate = self
    [self presentViewController:vc animated:YES];
}

Finally, implement the protocol in MenuViewController, and you're ready to go:

@interface MenuViewController : UIViewController<PlayerViewControllerDelegate>

@end

@implementation MenuViewController

-(void)playerViewControllerViewDidLoad:(PlayerViewController*)playerVC
{
    [playerVC playVideo];
}

@end
Vinod Vishwanath
  • 5,821
  • 2
  • 26
  • 40
2

It is usually not good practice to have one controller controlling the behavior of another. Instead, you can have the MenuViewController create the PlayerViewController and set variables so the new player knows how to behave based on its internal state.

There are several UIViewController methods that you can override in order to perform actions during the controller's lifecycle. Based on your question it seems like you want the viewDidLoad method.

I am not sure how you are passing videos between controllers, but if you were using URLs (to Youtube videos for example) then you could do something like the following:

// MenuViewController.m
- (IBAction)videoOne:(id)sender {

    PlayerViewController* vc = [[PlayerViewController alloc] initWithNibName:@"PlayerViewController" bundle:nil];

    // Pass any necessary data to the controller before displaying it
    vc.videoURL = [self getURLForSender:sender];

    [self presentViewController:vc animated:YES completion:nil];
}

// PlayerViewController.m
- (void)viewDidAppear {
    // View did appear will only be called after the controller has displayed
    // its primary view as well as any views defined in your storyboard or 
    // xib. You can safely assume that your views are visible at this point.

    [super viewDidAppear];

    if (self.videoURL) {
        [self playVideo];
    }
}

You would need to define the property videoURL on PlayerViewController and expose it publicly. If you are using local files (such as from the user's photo storage) you could pass the video to the new view controller before presenting it.

There are other UIViewController lifecycle methods that you can override. They are explained in more depth in this post as well as Apple's UIViewController Documentation.

Edit: changed presentModalViewController:animated: to presentViewController:animated:completion: and changed viewDidLoad to viewDidAppear as it seems more appropriate for the question.

Community
  • 1
  • 1
Tom Leu
  • 142
  • 8
1

presentModalViewController:animated: is deprecated. Use presentViewController:animated:completion: instead.

In the completion Block, you can call a method on the presented view controller:

[self presentViewController:otherVC
                   animated:YES
                 completion:^{ [otherVC startPlaying]; }];

The completion is run after the presented controller's viewDidAppear.

jscs
  • 63,694
  • 13
  • 151
  • 195
  • I didnt know that i can call it in the completion block. is this an alternative to delegates in this case? – Teja Nandamuri Jan 07 '16 at 19:37
  • It's not as flexible, @Mr.T: you can't really get any information back since the Block runs immediately after the view appears, but you can do something simple like this. – jscs Jan 07 '16 at 19:41