Observing currentPlaybackTime and showing an overlay at intervals - ios

I’m making a video that shows overlays at certain intervals of a video. I’ve managed to get the video to play. Now my objective is to watch/observe the currentPlaybackTime value and pause the video when it hits 2 seconds.
After some research found that currentPlaybackTime does not support KOV. So I need to implement this solution but I have no idea where to put the code - I’m very new to Objective C. I keep trying to put it in the ViewController (My only view) but the way its written hints to it being placed somewhere else…
Should I create a new controller for it? Or do I override methods from the MediaPlayer framework?
Here's my code:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) MPMoviePlayerController *movieController;
#property(nonatomic) NSTimeInterval currentPlaybackTime;
#end
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidAppear:(BOOL)animated {
self.movieController = [[MPMoviePlayerController alloc] init];
NSString *moviePath = [[NSBundle mainBundle] pathForResource:#"Movie" ofType:#"m4v"];
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
self.movieController = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
[self.movieController.view setFrame:CGRectMake (0, 0, 480, 326)];
[self.view addSubview:self.movieController.view];
[self.movieController play];
[self.movieController currentPlaybackTime];
}
#end

You can implement following method in your found solution in this same view controller.
Right after
[self.movieController play];
call following method
[self BeginPlayerPolling];
Register this class as an observer before in your viewDidAppear where you initialised your movieController
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(movieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object: self.movieController];
self.movieController = [[MPMoviePlayerController alloc] init];
and implement this notification observer's method
- (void)movieFinishedCallback:(NSNotification*)aNotification
{
// Remove the movie player view controller from the "playback did finish" notification observers
[[NSNotificationCenter defaultCenter] removeObserver: self
name:MPMoviePlayerPlaybackDidFinishNotification
object: self.movieController];
[self EndPlayerPolling];
}

Related

Audio Player Progress slider with timer displaying in objective c

I am new from iOS app development ,my requirement is need access the progress slider while playing , and show the proper start and end time , the player is working in url streaming , can you please help me to give some sample code , to fix this issue , Thanks in advance.
Please find the below code. i have tested in Xcode 8 beta 2
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
ViewController.m
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#interface ViewController ()
{
IBOutlet UISlider *sliderView;
IBOutlet UILabel *lblTimeInterval;
AVAudioPlayer *avpPlayer;
}
#end
#implementation ViewController
#synthesize strName;
#synthesize strTitle;
#synthesize trackCount;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//First comment added to Proj.
//New Branch one is created by Team.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)progrssSlider {
avpPlayer.currentTime = sliderView.value;
}
- (void)updateTime:(NSTimer *)timer {
sliderView.value = avpPlayer.currentTime;
lblTimeInterval.text = [NSString stringWithFormat:#"%f",sliderView.value];
}
- (IBAction)pressPlayButton:(id)sender {
//Read sound file from resource folder
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"sample.mp3" ofType:nil]];
//Initialize AVAudioPlayer
NSError *error;
avpPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
//Check Player is initialized
if (!avpPlayer){
NSLog(#"Error: %#", error);
}
else
{
[avpPlayer prepareToPlay];
sliderView.maximumValue = [avpPlayer duration];
sliderView.value = 0.0;
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTime:) userInfo:nil repeats:YES];
[avpPlayer play];
}
}
#end
Please define UILAbel to show Time, UISlider to Update Slider and Play button in Xib.
Hope this will work.

How can I specify a target for my action to point to?

I'm learning objective-c for iOS and have a question about creating my first target-action mechanism. I've got it to work, but currently I just set the target: portion of the addTarget:action:changeForControlEvents: method to nil, meaning it will search around my app for the target instead of drilling down on ViewController.m, where the method I want to send a message is located.
How can I tell the addTarget:action:changeForControlEvents: method which class to search first?
Here is a simple version of my current code:
The view:
// View.m
#import View.h
#implementation
- (void)sendAction
{
UIControl *button = [[UIControl alloc] init];
[button addTarget:nil // How do I make this look for ViewController.m?
action:#selector(changeButtonColor)
changeforControlEvents:UIControlEventTouchUpInside];
}
#end
...and the View Controller:
// ViewController.m
#import ViewController.h
#implementation
- (void)target
{
NSLog(#"Action received!");
}
#end
Thanks for the help!
You cannot simply called UIViewController if it doesn't load or allocate in the memory. To achieve this you need to alloc that class.
one way to do using singleton
[button addTarget:[ViewController sharedManager] action:#selector(target)
forControlEvents:UIControlEventTouchUpInside];
or using NSNotificationCenter, assuming that the class is already running (stack in previous navigation / another tabs).
// View.m
#import View.h
#implementation
- (void)sendAction
{
UIControl *button = [[UIControl alloc] init];
[button addTarget:self
action:#selector(controlAction)
changeforControlEvents:UIControlEventTouchUpInside];
}
-(void)controlAction
{
[[NSNotificationCenter defaultCenter]
postNotificationName:#"changeButtonColor"
object:self];
}
#end
and for target UIViewController
// ViewController.m
#import ViewController.h
#implementation
-(void) viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"changeButtonColor"
object:nil];
- (void)receiveNotification:(NSNotification *) notification
{
NSLog(#"Action received!");
}
#end
Assuming ViewController is the VC that created the view you're working in you should be able to use:
addTarget:[self superview]

Xcode: Unable to stop AVAudioPlayer music playing from sub class

STILL NO SOLUTION - REDUCED TEST CASE PROJECT HERE:
http://www.friendlycode.co.uk/assets/Bugfix.zip
I'm new to Xcode/Objective C and have done a lot of research but cannot find an answer. There are loads of similar questions here but none of them have helped me solve this.
Files:
app.h
app.m
Settings.h
Settings.m
I have some background music playing which starts when the app is launched via ViewDidLoad in the ViewController.m file.
I am trying to stop this from the Settings.m file if the Music switch is touched and set to off.
Please see code below (have removed unnecessary outlets/methods etc)
The NSLog outputs 'attempting to stop audio' but audio is not stopped. I think I have referenced the ViewController class correctly so unsure why it won't stop?
app.h
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#import "AVFoundation/AVAudioPlayer.h"
#interface ViewController : GAITrackedViewController <AVAudioPlayerDelegate, UIActionSheetDelegate>
{
// removed
}
#property (nonatomic, retain) AVAudioPlayer *BackgroundMusicPlayer;
#end
app.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Play Background music
[self PlayBackgroundMusic];
}
-(void)PlayBackgroundMusic
{
NSString* resourcePath = [[NSBundle mainBundle]
pathForResource:#"music-file"
ofType:#"aiff"];
NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
_BackgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
_BackgroundMusicPlayer.delegate = self;
[_BackgroundMusicPlayer play];
_BackgroundMusicPlayer.numberOfLoops = -1;
_BackgroundMusicPlayer.currentTime = 0;
_BackgroundMusicPlayer.volume = 0.5;
}
}
Settings.h
#import <UIKit/UIKit.h>
#import "app.h"
#interface Settings : GAITrackedViewController <AVAudioPlayerDelegate, UIActionSheetDelegate>
{
IBOutlet UIButton *BackButton;
IBOutlet UISwitch *MusicSwitch;
IBOutlet UISwitch *SoundFXSwitch;
// Get instance of ViewController object
ViewController *home;
}
-(IBAction)BackButton:(id)sender;
-(IBAction)ToggleMusic:(id)sender;
-(IBAction)ToggleSoundFX:(id)sender;
#end
Settings.m
#import "Settings.h"
#interface Settings ()
#end
#implementation Settings
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)ToggleMusic:(id)sender {
// Get instance of ViewController object
//home = [[ViewController alloc] init];
if (MusicSwitch.on)
{
[home.BackgroundMusicPlayer play];
}
else {
[home.BackgroundMusicPlayer stop];
NSLog(#"Attempting to stop audio");
}
}
-(IBAction)ToggleSoundFX:(id)sender {
if (SoundFXSwitch.on)
{
}
else{
}
}
-(IBAction)BackButton:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
I think the problem is with the ViewController *home.
Your AvAudioPlayer object is in the app.h in the interface ViewController.
But in your code , you are not initialising the ViewController object "home"
in settings.m. So effectively , you are trying to access and stop a player that
is not created.
To access the AVAudioPlayer object add the following code in your viewDidload of settings.h.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//initialise the audioPlayer object.
home=[[ViewController alloc] init];
}
In your Settings.h , declare the ViewController property as assign
#interface Settings : GAITrackedViewController <UIActionSheetDelegate>
{
#property (nonatomic, assign) ViewController *home;
}
-(IBAction)ToggleMusic:(id)sender {
if (MusicSwitch.on)
{
[self.home.BackgroundMusicPlayer play];
}
else {
[self.home.BackgroundMusicPlayer stop];
}
}
From your app.m , assign the home property as self _BackgroundMusicPlayer.home = self;
-(void)PlayBackgroundMusic
{
NSString* resourcePath = [[NSBundle mainBundle]
pathForResource:#"music-file"
ofType:#"aiff"];
NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
_BackgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
_BackgroundMusicPlayer.delegate = self;
settingsViewObj.home = self; //recommended after the 'Settings' view allocation code.
[_BackgroundMusicPlayer play];
_BackgroundMusicPlayer.numberOfLoops = -1;
_BackgroundMusicPlayer.currentTime = 0;
_BackgroundMusicPlayer.volume = 0.5;
}
}
Notes:
Read more about object communication
Read more about Objective C coding standards
Read more about class hierarchy
If I am understanding your code correctly, it seems you are creating a instance of your initial view controller and trying to stop the music player property of that instance. If that is the case, the music player you are trying to stop is already stopped, because it is a a separate instance of AVAudioPlayer that was created when you created an instance of your ViewController. In order to stop the music player from the first view controller, you could try this:
In the Settings.h file, add an AVAudioPlayer property just like in app.h
#property (strong, nonatomic) AVAudioPlayer *backgroundMusic;
Then when segueing to the settings view controller, pass the audio player to the new controller using prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"YourSegueName"]) {
if([segue.destinationViewController isKindOfClass:[YourSettingsClass class]]) {
YourSettingsClass *destination = segue.destinationViewController;
destination.backgroundMusic = self.BackgroundMusicPlayer;
}
}
}
You should now be able to simply call [self.backgroundMusic stop] and stop your tunes.
Ensure that you #import your Settings controller class in your app class to access it in the prepareForSegue method.
You can't access the instance of an object of another class created by it, by importing
it.Here You have to access the same object instance , in order to stop the AVAudioPlayer.
So you have to place the object somewhere unique, like AppDelegate.
Try declaring the AVAudioPlayer in the appdelegate.h.
In Appdeleagte.h
#property (nonatomic, strong) AVAudioPlayer *BackgroundMusicPlayer;
and in your app.h you can access the player as follows.
AppDelegate *appDelegate;
//in your viewDidLoad
appDelegate=[[UIApplication sharedApplication]delegate];
//in your PlayBackGroundMusic
appdelegate.avAudioPlayer=[[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
[appDelegate.avAudioplayer play];
in your settings.h
AppDeleagte *appDelegate;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//initialise the audioPlayer object.
appDelegate=[[UIApplication sharedApplication]delegate];
}
you can stop the player by
[appDelegate.avAudioPlayer stop];
You can download the fixed project here

iOS: Playing a video on application launch

I am new to Xcode so I've been trying to follow along to tutorials but haven't come across any that explain what I'm trying to achieve. I just want to play a video automatically when you open an application. I have it somewhat working in that the audio plays, but I cannot see any video. Am I missing something? I am getting this Warning in my output window:
Warning: Attempt to present MPMoviePlayerViewController: 0x831d7a0 on ViewController: 0x9d10540 whose view is not in the window hierarchy!
In my ViewController.h:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#interface ViewController : UIViewController
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url =[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"Placeholder" ofType:#"mp4"]];
MPMoviePlayerViewController *playercontroller = [[MPMoviePlayerViewController alloc] initWithContentURL:url];
[self presentMoviePlayerViewControllerAnimated:playercontroller];
playercontroller.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[playercontroller.moviePlayer play];
playercontroller = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Instead of
[playercontroller.moviePlayer play];
do
[playercontroller play];
Side note:
Also check up on your naming convention. In Objective-C variable names "should" have capital letter on each new word in the variable. E.g. instead of playercontroller use playerController.
I know your status.
it's warning from Xcode.
So, you just change the code.
like this,
[playercontroller performSelector:#selector(player)];

Trying to use an NSTimer

I'm having a problem getting an NSTimer to work, probably because I don't know how to properly use it (I've tried reading the apple documentation, it didn't help me much). I get a time off of a UIDatePicker and I want the app to call the method when that time is reached. Simple, no? So the code I have is below:
-(IBAction)SetButtonPress:(id)sender{
NSDate *date = [Picker date];
alarmTimer = [[AlarmTimer alloc] init];
[alarmTimer runTimer: Picker.date];
}
A few things now follow standards, but it still doesn't work. It still gives me a time 6 hours ahead, and when it hits that time, it doesn't do anything.
And the code for the RunTimer method of my alarmTimer class is as follows:
-(void)RunTimer: (NSDate *) date
{
NSRunLoop *theLoop = [NSRunLoop currentLoop];
[theLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer initWithFireDate:date interval:0 target:self selector:#selector(Play) userInfo: nil repeats:NO];
}
A few things now follow standards, but it still doesn't work. It still gives me a time 6 hours ahead, and when it hits that time, it doesn't do anything.
And just in case it helps, here is the .h and .m files for the view controller:
.h:
//
// Assignment_1ViewController.h
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AlarmTimer.h"
#interface Assignment_1ViewController : UIViewController {
UIButton *SetButton;
UIDatePicker *Picker;
UIButton *CancelButton;
NSString *time;
AlarmTimer *alarmTimer;
}
#property (nonatomic, retain) IBOutlet UIButton *SetButton;
#property (nonatomic, retain) IBOutlet UIDatePicker *Picker;
#property (nonatomic, retain) IBOutlet UIButton *CancelButton;
- (IBAction)SetButtonPress:(id)sender;
- (IBAction)CancelButtonPress:(id)sender;
#end
.m:
//
// Assignment_1ViewController.m
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "Assignment_1ViewController.h"
#import "AlarmTimer.h"
#implementation Assignment_1ViewController
#synthesize Picker;
#synthesize CancelButton;
#synthesize SetButton;
- (void)dealloc
{
[SetButton release];
[Picker release];
[CancelButton release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidUnload
{
[self setSetButton:nil];
[self setPicker:nil];
[self setCancelButton:nil];
[super viewDidUnload];
[alarmTimer release];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)SetButtonPress:(id)sender {
NSDateFormatter *ndf = [[NSDateFormatter alloc] init];
time = [ndf stringFromDate:self.Picker.date];
alarmTimer = [AlarmTimer alloc];
[alarmTimer init];
NSLog(#"Date is: %#", [ndf dateFromString:time]);
[alarmTimer RunTimer:[ndf dateFromString:time]];
}
- (IBAction)CancelButtonPress:(id)sender {
}
#end
And the code for the AlarmTimer class:
.h:
//
// Timer.h
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#interface AlarmTimer : NSObject {
NSString *time;
NSTimer *timer;
AVAudioPlayer *player;
}
-(void) RunTimer: (NSDate *)date;
-(void)Play;
-(void)CancelTimer;
#end
And finally, the .m:
//
// Timer.m
// Assignment 1
//
// Created by Jack Schaible on 11-09-28.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "AlarmTimer.h"
#implementation AlarmTimer
-(id) init
{
if(self == [super init])
{
timer = [NSTimer alloc];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/Time.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
}
return self;
}
- (void) RunTimer: (NSDate *)date
{
[timer initWithFireDate:date interval:86400 target:self selector:#selector(Play) userInfo:nil repeats:NO];
NSLog(#"Date is: %#", date);
}
-(void) Play
{
[player play];
UIAlertView *aView = [[UIAlertView alloc] initWithTitle:#"Holy Chimes!" message:#"If you didn't hear that..." delegate:self cancelButtonTitle:#"Okay" otherButtonTitles:nil, nil];
[aView show];
}
-(void)CancelTimer
{
time = nil;
}
#end
Thanks a lot to anyone who can help!
Why do you use NSDateFormatter? try to do it like that:
alarmTimer = [AlarmTimer alloc];
[alarmTimer init];
NSLog(#"Date is: %#", self.Picker.date);
[alarmTimer RunTimer: self.Picker.date];
As a rule, do the +alloc and -init together: Foo *someFoo = [[Foo alloc] init];. Don't separate the calls as you've done with your timer and alarmTimer variables. The reason for this is that sometimes the initializer will return a different pointer than what you get from +alloc, and when that happens you want to be certain to store that new pointer. I don't know if NSTimer ever does that, but if so you'll have all kinds of trouble with your current code.
Don't try to reuse a timer. Once it fires, release it and create a new one when you need to.
What's all that craziness in -SetButtonPress:? You get a date from a date picker, convert it to a string, and then immediately convert that string back to a date... why?
You're using an interval of 24 hours (86400 seconds) for your timer. Are you under the mistaken impression that a timer with a long interval will wake up an app that's in the background? NSTimer relies on the run loop; when the run loop isn't running, the timer isn't working.
It'd be somewhat easier to help you if you'd stick to the usual Objective-C convention and name methods starting with lower-case characters, i.e. -runTimer: instead of -RunTimer:. The same rule applies to instance variables.

Resources