Weird behavior running MPMoviePlayerController in viewDidLoad - ios

I'm using the MediaPlayer framework to play a pretty sizeable movie (~200 MB) as soon as my application is launched. When I attempt to play the video in my viewDidLoad, breakpoints indicated that the view was added however the video did not show up on the device.
I then set up a button to confirm that the video worked at all. Running the IBAction from the button showed no problems.
So then I was stumped. And I wondered if it had anything to do with the fact that the video was being called as soon as the application was launched. So I added a NSTimer with a delay of five seconds to the viewDidLoad call. Lo and behold, it worked fine. Can anyone shed any light on to why the video won't play unless there is an initial delay? Code below.
- (void)viewDidLoad {
NSTimer *currentTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(playMovie) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:currentTimer forMode:NSRunLoopCommonModes];
[super viewDidLoad];
}
-(IBAction)playMovie
{
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"Speed" ofType:#"mov"];
NSURL *fileURL = [NSURL fileURLWithPath:filepath];
MPMoviePlayerController *moviePlayerController = [[MPMoviePlayerController alloc] initWithContentURL:fileURL];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlaybackComplete:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayerController];
[self.view addSubview:moviePlayerController.view];
moviePlayerController.fullscreen = YES;
[moviePlayerController play];
}

Try playing the movie in viewDidAppear: instead of viewDidLoad. At the point viewDidLoad is called, the view is not yet in the view hierarchy. You could still create the MPMoviePlayerController in viewDidLoad, just don't play it until the view actually appears.

I think the size of the video has something to do here. I tried doing the same thing with a smaller video and it works fine. It seems the video is loaded first and then played ( see this stack over flow question: MPMoviePlayerController not showing controls until video is loaded). Apparently, the 200 MB video takes 4-5 seconds to preload. Try the code that was'nt working with a video of smaller size. If it works fine, then it's definitely the size problem.
Edit: I found this in MPMoviePlayerController Class Reference "If you know the source type of the movie, setting the value of this property before playback begins can improve the load times for the movie content. If you do not set the source type explicitly before playback, the movie player controller must gather this information, which might delay playback." I notice you haven't declared source type. Add to that the size of the video, and you've got yourself 4-5 seconds of load time.
Also try this before playing [moviePlayerController prepareToPlay];

I had a different experience but moving the code in viewDidAppear: helped my case as well. I have IOS code that segues from one storyboard to another and once the segue is complete - in the viewDidLoad: I was initializing a periodic timer. The code for the timer is below [I was 'calling' startTimer in viewDidLoad:]:
// Schedules a new timer, adds it to the current run loop and waits forever.
- (void) startTimer
{
_timer = [NSTimer scheduledTimerWithTimeInterval: 10.0
target:self
selector:#selector(request)
userInfo:nil
repeats:YES];
// [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
// [[NSRunLoop mainRunLoop] runUntilDate:[NSDate distantFuture]];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
What is interesting is that the when I call the startTimer in viewDidLoad: I can never observe the view on the iPhone change to the next controller - I can see the NSLog statement that shows the control came to
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
but the control does not go to the ViewController.
This behavior is independent of the timing of the periodic timer - setting low or high does not impact the problem. The observed problem goes away when I move the startTimer invocation to viewDidAppear:
This is followed by another set of challenge even when I move the timer code to viewDidAppear:. This code of having a regular timer - I have created this so that I can get the RSSI value for the Bluetooth Low Energy Connection. What I have observed is that I stop getting delegate callbacks for the Bluetooth delegate.

Related

NSTimer does not call selector after assigned time interval. iOS

I am having a banners array and displaying it in my custom cell. When user clicks on banner, I push a detailViewController and opens in-app browser.
I am changing banners after every 5 seconds. For that, I am using NSTimer to schedule the selector call. Everything works great.... Until, user clicks on banner and come back from detailViewController. When user comes back, NSTimer behaves really weird. It changes first banner after 5 seconds (as assigned) and then next banner is changed after 1 second and so on.
Her is the code I am using:
#pragma mark - User Methods
-(void) resetBannerRotationTimer {
[self.bannerTimer invalidate];
self.bannerTimer = nil;
self.timeInterval = 5.0f;
self.bannerTimer = [NSTimer scheduledTimerWithTimeInterval:self.timeInterval target:self selector:#selector(rotateBanner) userInfo:nil repeats:YES];
self.timeInterval = self.bannerTimer.timeInterval;
[[NSRunLoop currentRunLoop] addTimer:self.bannerTimer forMode:NSRunLoopCommonModes];
}
rotateBanner:
-(void) rotateBanner {
BannerCell *bannerCell = (BannerCell *)[self.dealsTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[bannerCell updateBanner];
}
In my updateBanner method, I am handeling UIPageControl to change pages. (I don't think that code needs to be posted).
I am calling resetBannerRotationTimer method in viewWillAppear method.
i think you are missing to fire your timer
add this line after timer scheduling
[self.bannerTimer fire];

NSTimer does not stop

[self performSelectorInBackground:#selector(method) withObject:nil];
-(void)method
{
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1) userInfo:nil repeats:YES];
runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer1 forMode:NSRunLoopCommonModes];
[runLoop run];
}
-(void)viewdidunload
{
[timer1 invalidate];
timer1=nil;
}
I start Timer in HomeViewController even I invalidate, it keeps running in OtherViewController. What is wrong with my code?
First of all, when you're overriding life cycle methods, you should include a call to the super version of that method.
Second of all, Objective-C is case sensitive, so even if your app would try to call the life-cycle even, viewDidUnload, your method would simply never be called because that's what you titled your method.
Third of all, viewDidUnload was deprecated in iOS 6.0 and shouldn't be used at all by this point unless you're going way out of your way to support backward compatibility. It will never be called in iOS 6.0 and greater.
If you want the timer to stop when the user navigates away from the current view, you'll want something like this:
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
if (timer1.isValid) {
[timer1 invalidate];
}
timer1 = nil;
}
If you're looking for something else, you'll need to elaborate on what it is you want to accomplish exactly.
If you ARE working on a pre-iOS 6.0 project, for whatever reason, the reason your method isn't being called is at least in part because it is spelled wrong. Again, Objective-C is case sensitive. Your method name should be spelled viewDidUnload.
For future reference, the question shouldn't really be "why isn't my timer invalidating?" You should have start by using breakpoints or NSLog statements to determine whether or not your method, viewdidunload, which tries to invalidate the timer even fires. When you find out it's not being called, do a search to ask "How come viewdidunload isn't called?" Then you'll go fix the capitalization problem and the problem will (probably) remain, so do some more research. And if at the end, you still can't figure it out, as a worst case scenario, the post question should be "how come viewdidunload isn't called?"
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1:) userInfo:nil repeats:YES];
set colon for function in selector
-(void) getLastImageName1 :(NSTimer*)timer1
{
//Do your all process and invalidate after completion
[timer1 invalidate];
}
or if you want to remove timer after moving to next view controller use how #nhgrif mentioned
- (void)viewDidDisappear:(BOOL)animated
{
[timer1 invalidate];
}
[self performSelector:#selector(method) withObject:nil afterDelay:0.0];
-(void)method
{
timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(getLastImageName1) userInfo:nil repeats:YES];
runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer1 forMode:NSRunLoopCommonModes];
[runLoop run];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[timer1 invalidate];
timer1=nil;
}
There is no need to add the timer (again) on the main run loop. Or is it necessary for You to run it also in commonModes? For me it was never necessary.
From the NSTimer Documentation:
scheduledTimerWithTimeInterval:invocation:repeats:
Creates and returns a new NSTimer object and schedules it on the
current run loop in the default mode.
Since the NSRunLoop Documentation points out that you can add timer on several run loop modes, maybe this is the problem.
addTimer:forMode:
Discussion You can add a timer to multiple input modes. While running
in the designated mode, the receiver causes the timer to fire on or
after its scheduled fire date. Upon firing, the timer invokes its
associated handler routine, which is a selector on a designated
object.
Also I don't get why you are invoking the timer creation with performSelector?
I just wrote a minimalistic sample. thats totally working!
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(doWork:) userInfo:Nil repeats:YES];
}
- (void)viewDidDisappear:(BOOL)animated{
[self.timer invalidate];
[super viewDidDisappear:animated];
}
- (void) doWork:(id) userInfo
{
NSLog(#"Working again");
}
Hope this helps.
-(void)viewDidUnload is a delegate which fires on memory warning only and is deprecated after iOS 6. It will also never fire on the simulator.
Stop timer in
- (void)viewWillDisappear:(BOOL)animated
or in
- (void)viewDidDisappear:(BOOL)animated

iOS AVAudioPlayer does not play while finger panning MKMapView

I have a basic beep sound that I loaded with AVAudioPlayer in my app.
It can play the beep fine if my finger isn't panning my MKMapView.
My beep is set to play every 2 seconds.
When I start panning the map view, and don't take my finger off the screen, the beep stops playing.
I recall NSUrlConnection also doesn't fire while scrolling a table view, I thought this might be the same problem but I couldn't figure out how I can add my audio player to the proper run loop.
I setup my player like this:
-(void)setupBeep
{
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/beep.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
if(error)
{
NSLog(#"Error opening sound file: %#", [error localizedDescription]);
}
}
and I am playing my sound like this:
// plays a beeping sound
-(void)beep
{
[audioPlayer play];
}
Anyone came across this problem before?
How have you scheduled your -beep method to be called?
I suspect you’ve added an NSTimer to the main run loop in the default input mode. The reason you’re not hearing anything is that while MKMapView is tracking your finger, the run loop is in not in NSDefaultRunLoopMode—it’s in UITrackingRunLoopMode.
Try scheduling in NSRunLoopCommonModes, which includes both default and tracking modes:
NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:self selector:#selector(beep) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Also, be aware that NSTimer retains its target. A repeating timer will reschedule itself automatically, so you’ll need to call its -invalidate method to remove it from the loop and allow the target to be released.
EDIT: Check out Apple’s Threading Programming Guide: Run Loops for a much more detailed explanation.

AVPlayer Statement for buffering

I am using the AVPlayer to play a live stream from the Internet, and need to show than the player is buffering :
I am using an NStimer :
timer = [NSTimer scheduledTimerWithTimeInterval:(1.0/2.0) target:self selector:#selector(buffering) userInfo:nil repeats:YES];
-(void)buffering {
if(radiosound.rate == 1.0)
[activityIndicator stopAnimating];
else
[activityIndicator startAnimating];
}
For sure rate property is not working properly to show !
Is it an other statement to know if the AVPlayer is buffering ?
You need to look at setting up key-value observers for the loaded times, and use that to figure out if you are waiting for data at the current play point.
Observers are setup using addObserver:self forKeyPath: options: context: method on AVPlayerItem and then in the observeValueForKeyPath: ofObject: change: context: callback you can figure out what times have been loaded compared with where in the item the player is playing.
You won't see the rate variable drop to zero when buffering, as this is the desired playback rate, not the actual rate being achieved.
Use the NSURLConnection class in conjunction with the NSURLConnectionDataDelegate protocol.

Why would my timer work on the iPad simulator but not on the iPad?

I'm working with all UIKit, and—for prototyping purposes have just shoved all the logic into the main viewController. I create some UIViews in viewDidLoad, hide some, and then set an NStimer to unhide the hidden ones in 4 seconds. This timer fires perfectly in the simulator, but will never fire on the iPad. Why could this happen and what should I even be looking for?
This is where I set my view and timer.
- (void)viewDidLoad {
[super viewDidLoad];
//snipped out long code that adds UIViews as subviews and runs fine
curtainView.hidden=YES;
questionLabel.hidden=YES;
[NSTimer scheduledTimerWithTimeInterval:4
target:self
selector:#selector(dropCurtain:)
userInfo:nil
repeats:NO];
[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(askQuestion:)
userInfo:nil
repeats:NO];
}
And here's the function that gets called when the first timer is up. This doesn't run on the iPad.
-(void)dropCurtain:(NSTimer *)timer{
curtainView.hidden=NO;
//curtainView.alpha=.5;
[self.view bringSubviewToFront:curtainView];
[self.view bringSubviewToFront:triesLabel];
}
Your callback might be called not in the main thread, ui changes call only works from the main thread. If this is the case there's a method of nsobject to schedule a call to selector in the main thread, and you can call this from your callback to manipulate ui
(sorry not near my work computer)

Resources