Play Video immediately when UIView is loaded - objective-c

Howdy! I'm writing an iPad app, and I need to be able to play a video when a UIView loads. However, I was getting a BAD_EXC_ACCESS if I try to message my MPMoviePlayerController anywhere after I initialize it. I removed the MPMediaPlayerController from my *.h file, then declared it entirely in the implementation file, and now I'm getting the message at the bottom below my code. There are no issues in Build and Analyze about memory leaks (or any issues, for that matter), and I cannot find any posts about this. Here's my code:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
NSString *url = [[NSBundle mainBundle] pathForResource:#"p0600c0100cmpintro" ofType:#"m4v"];
MPMoviePlayerController *movie = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
NSLog(#"%#", movie);
movie.view.frame = CGRectMake(5, 0, 1035, 768);
movie.view.contentMode = UIViewContentModeScaleToFill;
[[movie view] setCenter:CGPointMake(movie.view.center.x-10, movie.view.center.y)];
[movie setControlStyle:MPMovieControlStyleNone];
[movie setShouldAutoplay:YES];
[[self view] addSubview:[movie view]];
return self;
}
The NSLog of "movie" gives "MPMoviePlayerController: 0x1b77f0", but then the error message upon crash is "* -[MPMoviePlayerController playbackState]: message sent to deallocated instance 0x1473a0". Help?

According to the documentation it looks like the frame of the movie view needs to match the view of its parent. Also try moving your code out of the initWithNibName:bundle: into viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *movieContainer = [[UIView alloc] initWithFrame:CGRectMake(5, 0, 300, 400)];
//Do any other positioning of the view you would like
NSString *path = [[NSBundle mainBundle] pathForResource:#"p0600c0100cmpintro" ofType:#"m4v"];
NSURL *url = [NSURL fileURLWithPath:path];
MPMoviePlayerController *movie = [[MPMoviePlayerController alloc] initWithContentURL:url];
movie.view.frame = movieContainer.bounds; //Make sure this is the bounds of its parent view
movie.scalingMode = MPMovieScalingModeFill;
movie.controlStyle = MPMovieControlStyleNone;
movie.shouldAutoplay = YES;
[movieContainer addSubview:movie.view];
[self.view addSubview:movieContainer];
[movieContainer release];
}
One last suggestion would be to keep a reference of the movie so that you can dealloc it once the view controller gets deallocated

I would suggest creating your MoviePlayer in viewDidLoad, then in viewDidAppear make the movie play, for best results.

Alright, so I have another MPMoviePlayerController instance which was being deallocated earlier, but when I try to create another instance of MPMoviePlayerController, all the messages sent to this one were being sent to the deallocated instance, resulting in the memory problem. So I just removed the part where I released the first instance, and it works perfectly fine. My question now is this: is there a way to deallocate this first instance so that it isn't a burden on memory when it isn't needed? I feel that there should be a more elegant solution to this problem. I will need to play videos frequently in this application.

I found the solution a number of weeks ago and forgot about this post. I wasn't successfully releasing the MPMoviePlayerController. For those curious, in order to release an MPMoviePlayerController, we must first remove the notification from the NSNotificationCenter (if one was set), stop the movie (even if it's done playing), THEN release it. I wasn't doing this earlier in the application with my first MPMoviePlayerController, so it was trying to reference the deallocated instance. When the movie is done playing, here is the code to release the movie successfully:
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:movie];
[movie.view removeFromSuperview];
[movie stop];
[movie release];

Related

Initiate self.window in appDelegate init method

I'm a web developer creating an Apache Cordova application so my knowledge with Objective-C is very little. Everything is going fine until i try to supplement the splash screen with a video. It sort of does it, but not fully.. It starts with displaying the Default.png followed by the SplashScreenLoader. It then actually plays the video and I know this because the audio is emitted, but the video layer isn't shown.
What I've found out is that the self.window or self.viewController are both defined in didFinishLaunchingWithOptions, so they don't exist in the - (id) init method. Therefore I can't find a way to place it on top of the loading splash.
My init method currently looks like this in AppDelegate.m:
- (id) init {
NSString *moviePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"Splash_v1.mp4"];
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
MPMoviePlayerController* moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL: movieURL];
moviePlayer.controlStyle = MPMovieControlStyleNone;
[moviePlayer.view setFrame: self.window.bounds];
[self.window addSubview:moviePlayer.view];
[self.window makeKeyAndVisible];
[moviePlayer play];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
[cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
[CDVURLProtocol registerURLProtocol];
return [super init];
}
Here the self.window is null, and I've also attempted to set the self.window with this code:
CGRect screenBounds = [[UIScreen mainScreen] bounds];
self.window = [[[UIWindow alloc] initWithFrame:screenBounds] autorelease];
...without prevail. It actually sets it, but for the subsequent code it doesn't wanna do it.
So what I'm wondering is, how would I place this video on top of the splash's content, before didFinishLaunchingWithOptions kicks in?
Thanks in advance,
//Peter
So what I'm wondering is, how would I place this video on top of the splash's content, before didFinishLaunchingWithOptions kicks in?
actually, you do that in didFinishLaunchingWithOptions. put this statements in there (in the bolierplate code that Xcode generates for you, you should already have a call to makeKeyAndVisible, so just complement it):
[self.window addSubview:moviePlayer.view];
[self.window makeKeyAndVisible];
having previously instantiated your moviePlayer.
One way to avoid the blank screen could be this:
create a UIImageView containing your Default.png image;
display such UIImageView by adding it to your self.window as a subview (this will create no black screen effect);
initialize your player (I assume it takes some time, hence the black screen) and add it below the UIImageView;
when the player is ready (viewDidLoad) push it on top of the UIImageView.
Finally, I don't know how your player will signal the end of the video play, but I assume you have some delegate method; make you appDelegate be also your player delegate and from there, remove UIImageView and player from self.window and add you other view to self.window.
Hope this helps.
EDIT:
This is a rough sketch of what I would try and do in your app delegate appDidFinishLaunching:
self.moviePlayer = <INIT MOVIEW PLAYER CONTROLLER>
self.backgroundImage = <INIT UIImageView with Default.png>
[self.window addSubview:self.moviePlayer.view];
[self.window addSubview:self.backgroundImage];
[self.window makeKeyAndVisible];
In your movie Player viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
...
[self.view.superview addSubview:self.view]; //-- this will just move the player view to the top
...
}
If that helps anyone else, I had the same issue which was resolved by moving '[self.window makeKeyAndVisible];' above the subview like this, so in appdelegate.m:
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 300)];
myView.backgroundColor = [UIColor blueColor];
[self.window makeKeyAndVisible];
[self.window addSubview:myView];
This works fine, but if I swap the last two statements around, the subview does not display.

Unable to play local video from iPhone

I'm having trouble playing a video on the iPhone. I have a file in the app bundle and I am trying to play it in a MPMoviePlayerController but it is just displaying a black screen. This is the following code:
-(UIView*)createVideoPlayerOfWidth:(CGFloat)width
{
// The width for one of these can be half of the max width
//CGFloat widthAndHeight = width / 2.0f;
// TODO: Create a video player
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"myVideo" ofType:#"mov"]];
MPMoviePlayerController *playerController = [[MPMoviePlayerController alloc] initWithContentURL:url];
[playerController prepareToPlay];
[playerController setShouldAutoplay:NO];
[playerController setScalingMode:MPMovieScalingModeAspectFit];
[playerController setControlStyle:MPMovieControlStyleEmbedded];
[playerController setRepeatMode:MPMovieRepeatModeNone];
// Resize the thumbnail of the video
[[playerController view] setFrame:CGRectMake(0, 0, width, width)];
return [playerController view];
}
It is returning a valid URL (pathForResource would return nil if it couldn't find the file). I am displaying it to the screen by just adding a subView (the view returned by the function) to a scroll view. I've been trying to solve this for ages now and am getting nowhere. Any help is much appreciated.
Thanks!
Make sure you are retaining a reference to the MPMoviePlayerController instance:
If this is an ARC project, then playerController will be destroyed when createVideoPlayerOfWidth returns, and your video probably won't play. You should store the player in a strong instance variable or property.
You could add something like this to the top of your view controller file:
#interface MyViewController ()
#property (nonatomic,retain) MPMoviePlayerController *player;
#end
And insert this after you create the MPMoviewPlayerController instance:
self.player = playerController;
If you're not using ARC, this is probably not the problem, but you should still keep a reference in an instance variable, so that you can release it later.

Memory Crash in UIPopoverController

I've now invested days in trying to figure out what is going on and for the life of me I can't see what I am doing wrong. I am popping up a UIPopover when the user touches a point on the screen. The popover has a tab controller and table view that displays information about that point. But when the popover is dismissed, it crashes claiming that:
-[UIAnimator removeAnimationsForTarget:]: message sent to deallocated instance
Here is the code that loads the view controller:
MyViewController *popView = [[MyViewController alloc] init];
myPop = [[UIPopoverController alloc] initWithContentViewController:pop];
[popView release];
myPop.delegate = self;
[airportPop setPopoverContentSize:popView.view.frame.size];
[airportPop presentPopoverFromRect:CGRectMake(location.x,location.y,1,1) inView:self.mainView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
- (void)dismissPopover {
if( myPop != nil ) {
[myPop dismissPopoverAnimated:YES];
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
}
}
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
[myPop release];
myPop = nil;
}
The actual MyViewController is just a UIViewController that with (abridged for brevity) init:
- (id)init
{
self = [super init];
//create a newview
self.view = popView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, POPUP_HEIGHT)];
[popView release];
topBar = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, POPUP_WIDTH, 30)];
....
[popView addSubview:topBar];
[topBar release];
//create a table view
self.table = [[UITableView alloc] initWithFrame:CGRectMake(0, 30, POPUP_WIDTH, POPUP_HEIGHT-30-49)];
table.delegate = table.dataSource = self;
....
//create a tab bar
tabBar = [[UITabBar alloc] initWithFrame:CGRectMake(0, POPUP_HEIGHT-49, POPUP_WIDTH, 49)];
tabBar.delegate = self;
[popView addSubview:tabBar];
[popView addSubview:table];
[tabBar release];
[table release];
return( self );
}
Dealloc is nothing more than [super dealloc] since everything is essentially owned by the view and the view controller will take care of it. When the myPop is released, in DidDismissPopover, the view is also released, so that seems to work okay. But very soon thereafter, I get the crash.
Do I need to do something special to discard the tab view or table view when the popup dismisses?
I am using an autorelease on the cells in the table, should I stop doing that?
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
Thank you in advance for any help!!! Any ideas at all are greatly appreciated!!
-Kevin
[myPop dismissPopoverAnimated:YES] will continue to access you object even after the method call because you set YES for the animation (there is a timer and other stuff going under the hood to perform the animation for that)
So, instead of immediately releasing the object, you could mark it as autorelease to postpone this action, which actually might solved it or not.
Or postpone the release to a time after that makes tyou sure thta the animation will be finished. You could use GCD for that (if you are using iOS 4+) and as the default time for animation in UIKit is 0.3s, the code bellow should do the trick.
double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[myPop.delegate popoverControllerDidDismissPopover:airportPop];
});
EDIT: You should use this time only for the test propose as it is far from being the right way to release an object.
You should store a pointer to your UIPopover and release it in your class dealloc method.
Add following keys in yor Exectables info->Arguments tab-> enviroment variables
NSZombieEnabled = YES
CFZombie = 5
MallocStackLoggingNoCompact = 1
then when you get crash automatically you get a message
some thing like this
(gdb) continue
2011-06-09 11:46:08.404 test [6842:40b] * -[_NSArrayI
release]:message sent to deallocated instance 0X64a4900
then type
(gdb) info malloc-history 0x64a4900
it will give you complete history.
May be it helps you to find the place.
also you can use where command when you got crash.
The fastest way to avoid waiting for animation to end is to set popoverController.delegate = nil as soon as you dismiss the popup or the Popover Delegate method
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
is called.

Using Instruments to improve memory-management with modal view controllers

I feel like I don't understand something fundamental here. I've been working on memory management in my app while using Instruments to check out live allocations. I have a modal view controller (settingsViewController) that has an image for a background. One thing I noticed was that even after settingsViewController dealloc is called, there still is a live Malloc 520 KB, ImageIO is the responsible library. I'd expect live memory to drop back down after dismissing settingsViewController. Is the UIImageView still hanging around somewhere?
Here is how I load the image in viewDidLoad, as well as dismiss the view controller when I'm finished.
- (void)loadView {
[super loadView];
////// background ////////
UIImageView *background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"settings_background.png"]];
[self.view addSubview:background];
[background release];
//////////////////////////
}
- (void)viewDidLoad {
[super viewDidLoad];
///////// done button //////////
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneButtonPressed)];
self.navigationItem.leftBarButtonItem = done;
[done release];
////////////////////////////////
}
- (void) doneButtonPressed {
[self dismissModalViewControllerAnimated:YES];
}
- (void)dealloc {
NSLog(#"settingsViewController dealloc"];
[super dealloc];
}
At this point, this is all that is in the settingsViewController, so no need to do anything special in dealloc, right? Here is how I'm showing testViewController in the rootViewController.
- (void) loadSettingsView {
SettingsViewController *settingsViewController = [[SettingsViewController alloc] init];
UINavigationController *settingsNavigationController = [[UINavigationController alloc] initWithRootViewController:settingsViewController];
[self presentModalViewController:settingsNavigationController animated:YES];
[settingsViewController release];
[settingsNavigationController release];
}
I'd like to make sure I understand what is going on before moving forward. I have several different modal view controllers, each with a different image as a background. Since each one creates a Malloc 520 KB, I end up using 2-3 MB of precious memory for no good reason. What is holding on to that 520 KB?
When you use the +[UIImage imageNamed:] method, the framework caches the image data for you. That's why you see it hold onto some memory even after your view is released. If you're working in the simulator and you want to see it release that memory, send the simulator a memory warning after you've dismissed your view. The image framework should then release the cached image data.

Can't seem to be able to re alloc a released UIView

I have a UIView which I create programmatically roughly like so:
#implementation SlideShowView
- (id)initWithImages
{
…
if (self=[super initWithFrame:CGRectMake(0, 0, 320, 480)])
{
// init objects here
}
return self;
}
- (void)dealloc
{
printf("dealloc slide show view\n");
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"unlockUI" object:nil ];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"setUserRating" object:nil ];
[mCurrentImageView release];
[mRightImageView release];
[mLeftImageView release];
[mImages release];
[queue cancelAllOperations];
[queue release];
[managingArray release];
[super dealloc];
}
with a uiview controller that contains this:
- (void)loadView {
{
...
self.view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
self.slideShowViewObject = [[SlideShowView alloc] initWithImages];
[self.view addSubview:slideShowViewObject];
…
}
-(void)dealloc
{
[slideShowViewObject release];
[self.slideShowViewObject removeFromSuperview];
printf("dealloc of slideshow view controller\n");
[super dealloc];
}
this seems to be able to dealloc both the view controller and view but when I push this view controller back onto the stack I get the message with zombie enabled: -[SlideShowView retain]: message sent to deallocated instance 0x43ab160
should the alloc not be creating a new instance of the view when the controller is pushed? I don't understand and after spending many hours reading through other posts as well as looking at memory guides I would say I'm thoroughly stumped! Can someone offer any pointers? I only posted the code I deemed necessary but I can post more if need be.
Thanks so much!
First, you shouldn't call both [super init] and [super initWithFrame:], only one of the two in your UIView subclass.
Secondly, you should only set self.view in a view controller in the -loadView method, which is designed for you to create your views. Normally, unless you need to do certain types of setup such as initializing variables, etc, you shouldn't need to override -init in UIViewController subclasses.
finally, after finagling with the code again tonight I finally got it working. The key piece I was not releasing appears to be
[self.view release];
I suppose this makes perfect sense since I was adding the view as a subview of this alloc'd self view, but I had tried this before. Perhaps cleaning up the code is what made it work this time.