Is there any way to set specific audio file as tap sound in UIButtons across whole iOS app without writing code in individual view?
You don't want to add any code in subclass . so you can subclass UIApplication like :
#interface PlaySound : UIApplication
#end
#implementation PlaySound
- (BOOL)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event{
if ([sender isKindOfClass:[UIButton class]]) {
//here, play sound
}
return [super sendAction:action to:target from:sender forEvent:event];
}
#end
and register your own application in main.m like:
int main(int argc, char *argv[])
{
#autoreleasepool {
NSString *appClass = #"PlaySound";
int retVal = UIApplicationMain(argc, argv, appClass, NSStringFromClass([AppDelegate class]));
return retVal;
}
}
then whenever people touches an button control , your custom application will receive this message and play sound. you don't have to write any code in any individual view.
You can achieve this by writing code once in AppDelegate file
In AppDelegate.m write a class method, like given below
+(void)playAlarmSound
{
NSString *sound_file;
if ((sound_file = [[NSBundle mainBundle] pathForResource:#"Output" ofType:#"aif"])){
NSURL *url = [[NSURL alloc] initFileURLWithPath:sound_file];
if (audioPlayer)
{
[audioPlayer release];
audioPlayer= nil;
}
AVAudioPlayer *audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL]autorelease];
audioPlayer.delegate = self;
[url release];
audioPlayer.numberOfLoops=0;
[audioPlayer prepareToPlay];
[audioPlayer play];
}
}
In AppDelegate.h
+(void)playAlarmSound;
Now in your class where you want to call the above method write the below line
[AppDelegate playAlarmSound];
Note:- Import AVFoundation framework
I would say, create an 'Observer' class that plays the sound for all buttons are connected to. check this example.
creating a category is the way to go. answered by tiguero
.h:
#import <UIKit/UIKit.h>
#class SCLSoundEffect;
typedef enum {
SCLCLICKSOUND = 0,
SCLOTHERSOUND,
}
SCLSoundCategory;
#interface UIButton (soundEffect)
#property (nonatomic, strong) SCLSoundEffect *buttonSoundEffect;
+ (id) buttonWithType:(UIButtonType)buttonType andSound: (SCLSoundCategory)soundCategory;
- (void) playSound;
#end
.m:
#import "UIButton+soundEffect.h"
#import <objc/runtime.h>
#import "SCLSoundEffect.h"
static char const * const kButtonSoundEffectKey = "buttonSoundEffect";
#implementation UIButton (soundEffect)
#dynamic buttonSoundEffect;
+ (id) buttonWithType:(UIButtonType)buttonType andSound:(SCLSoundCategory) soundCategory;
{
UIButton *newButton = [UIButton buttonWithType:buttonType];
NSString *stringToUse = nil;
switch (soundCategory) {
case SCLCLICKSOUND:
stringToUse = #"button_sound.wav";
break;
case SCLOTHERSOUND:
assert(0); // To be defined
default:
break;
}
[newButton setButtonSoundEffect: [[SCLSoundEffect alloc] initWithSoundNamed:stringToUse]];
[newButton addTarget:newButton action:#selector(playSound) forControlEvents:UIControlEventTouchDown];
return newButton;
}
- (void) playSound
{
[self.buttonSoundEffect play];
}
- (SCLSoundEffect *)buttonSoundEffect {
return objc_getAssociatedObject(self, kButtonSoundEffectKey);
}
- (void)setButtonSoundEffect:(SCLSoundEffect *)buttonSoundEffect{
objc_setAssociatedObject(self, kButtonSoundEffectKey, buttonSoundEffect, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void) dealloc
{
[self setButtonSoundEffect:nil];
}
Now each time I create a button that play some sound I just need to use the following method:
UIButton *mySoundButton = [UIButton buttonWithType:UIButtonTypeCustom andSound:SCLCLICKSOUND];
Related
My problem is this, in the app when a user clicks somewhere not important an alertView is raised that's ok, I can find the call to that view, but then is showing again and again empty and I have placed breakpoint everywhere I see a call to any alert. But the ghost alert is not breaking anywhere I have no idea who is throwing it is just a sentient view.
Can you give some tips on how to pin point where is the view being called?
EDIT:
Code for the viewController:
#import <CoreLocation/CoreLocation.h>
#import "FormViewController.h"
#import "FormPageViewController.h"
#import "FormElement+UtilityMethods.h"
#import "UserBO.h"
#import "RecordBO.h"
#import "RecordAnswer.h"
#import "UserDefaultsUtilities.h"
#import "TimeTrackingUtilities.h"
#import "DxColors.h"
#import "EDQueueUtilities.h"
#import "GroupAnswerMetadata.h"
#import "RecordAnswer+UtilityMethods.h"
#import "Record+UtilityMethods.h"
#import "FormPageIndexViewController.h"
#import "ManagedObjectUtilities.h"
#import "EDQueue.h"
#import "EDQueueUtilities.h"
#import "DxAnswerObject.h"
#import "ImageAnswerMetadata.h"
#import "DateUtilities.h"
#import <ifaddrs.h>
#import "CarbonKit.h"
#define INITIAL_CONTROLLER_INDEX 0
#define FORM_RECORDS_TEMP_NAME #"<~TMP>"
#define TAG_RETURN_BUTTON 0
#define TAG_SAVE_BUTTON 1
#define TAG_SEND_BUTTON 2
typedef NS_ENUM(NSUInteger, AlertViewPurpose) {
ALERT_VIEW_FORM_NONE = 0,
ALERT_VIEW_FORM_SEND_SUCCESS = 1,
ALERT_VIEW_FORM_SEND_FAILURE = 2,
ALERT_VIEW_FORM_SAVE_PROMPT = 3,
ALERT_VIEW_FORM_FILE_NAME_PROMPT = 4,
ALERT_VIEW_FORM_ASYNC_SEND_SUCCESS = 5,
ALERT_VIEW_FORM_COULDNT_SEND = 6,
ALERT_VIEW_FORM_WANT_TO_SEND = 7,
ALERT_VIEW_FORM_SAVE_IN_CONTEXT_PROMPT = 8,
ALERT_VIEW_FORM_FILE_NAME_IN_CTXT_SAVE_PROMPT = 9,
ALERT_VIEW_FORM_REQUIRED_INTERNET_CONECTION = 10,
// Enumeration counter.
ALERT_VIEW_PURPOSE_COUNT
};
// Based on:
// Ref.: http://www.appcoda.com/uipageviewcontroller-storyboard-tutorial/
#interface FormViewController () <RecordBOProtocol, FieldElementProtocol,
CLLocationManagerDelegate, FormPageIndexProtocol,CarbonTabSwipeNavigationDelegate>
{
AlertViewPurpose _currentAlertViewPurpose;
CarbonTabSwipeNavigation *_carbonTabSwipeNavigation;
BOOL _unedited;
BOOL _formRecordNilAtStartUp;
BOOL _timestampTaken;
CLLocationManager *_locationManager;
CLLocation *_location;
NSDate *_timeSpentBaseTimestamp;
NSArray *_sortedPages;
NSUInteger _currentPageIndex;
NSString *formID;
NSArray *_pagesNames;
}
#property (weak, nonatomic) IBOutlet UILabel *lblFormTitle;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallReturn;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallSave;
#property (weak, nonatomic) IBOutlet UIButton *btnSmallSend;
#property (weak, nonatomic) IBOutlet UIButton *btnBigSend;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *btnReturn;
#property (strong, nonatomic) IBOutlet UIButton *lblBack;
#property (strong, nonatomic) IBOutlet UIButton *lblSave;
#end
#implementation FormViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
_currentAlertViewPurpose = ALERT_VIEW_FORM_NONE;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self localizedButtons];
// Starting up location manager if form requires it.
// Ref.:
// https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html#//apple_ref/occ/instm/CLLocationManager/requestAlwaysAuthorization
if ([self.form.geolocationEnabled boolValue]) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
if ([CLLocationManager locationServicesEnabled]) {
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
if (status == kCLAuthorizationStatusNotDetermined) {
// Requesting authorization.
if ([CLLocationManager instancesRespondToSelector:#selector(requestWhenInUseAuthorization)]) {
#ifdef DEBUG_MODE
NSAssert(
[[[NSBundle mainBundle] infoDictionary] valueForKey:#"NSLocationWhenInUseUsageDescription"],
#"For iOS 8 and above, your app must have a value for NSLocationWhenInUseUsageDescription in its Info.plist");
#endif // DEBUG_MODE
[_locationManager requestWhenInUseAuthorization];
}
} else if (status == kCLAuthorizationStatusAuthorizedAlways ||
status == kCLAuthorizationStatusAuthorizedWhenInUse) {
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
[_locationManager startUpdatingLocation];
}
}
}
self.lblFormTitle.text = self.form.name ;
// Saving whether self.formRecord was nil at beginning.
// Important for time spent tap calculations.
_formRecordNilAtStartUp = self.formRecord == nil;
[self setup];
//Take the time for counting
_timeSpentBaseTimestamp = [NSDate date];
_unedited = YES;
}
-(void)localizedButtons
{
[self.lblBack setTitle:NSLocalizedString(#"Back", #"Regresar") forState:UIControlStateNormal];
[self.lblSave setTitle:NSLocalizedString(#"Save", #"Guardar") forState:UIControlStateNormal];
[self.btnBigSend setTitle:NSLocalizedString(#"Send", #"Enviar") forState:UIControlStateNormal];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// Overriding from DxBaseViewController.
-(void)refresh
{
}
-(void)setup
{
// Obtaining sorted pages array.
_sortedPages = [[self.form.pages allObjects]
sortedArrayUsingComparator:^NSComparisonResult(Page *obj1, Page * obj2) {
return [obj1.pageNumber compare: obj2.pageNumber];
}];
//Adding toolBar
NSMutableArray *namesPages = [[NSMutableArray alloc]init];
for (Page *page in _sortedPages) {
NSString *namePage = page.name;
[namesPages addObject:namePage];
}
_pagesNames = [namesPages copy] ;
// Creating by default a record in case there's none.
if (self.formRecord == nil) {
self.formRecord = [Record createInContext:self.managedObjectContext];
// Filling in basic record information.
self.formRecord.name = FORM_RECORDS_TEMP_NAME;
self.formRecord.editable = self.form.editableRecords;
self.formRecord.dateLastSaved = self.formRecord.dateCreated = [NSDate date];
self.formRecord.syncStatusId = [NSNumber numberWithInt:SYNC_STATUS_NOT_SYNCED];
self.formRecord.user = [UserBO loggedInUser];
self.formRecord.form = self.form;
self.formRecord.formId = self.form.pkey;
self.formRecord.temporary = [NSNumber numberWithBool:YES];
self.formRecord.isBeingEdited = [NSNumber numberWithBool:YES];
// Committing record information as is. It will be removed if user doesn't
// want to save changes.
if (![Record commitChangesFromContext:self.managedObjectContext]) {
DebugLog(#"Temp form record couldn't be saved! Check!");
}
// Initializing page view controller.
_carbonTabSwipeNavigation =[[CarbonTabSwipeNavigation alloc] initWithItems:_pagesNames
delegate:self];
_carbonTabSwipeNavigation.toolbar.barTintColor = [DxColors colorWithHexRGB:NEW_FORMS_GREEN];
[_carbonTabSwipeNavigation setNormalColor:[UIColor whiteColor]];
[_carbonTabSwipeNavigation setIndicatorColor:[UIColor whiteColor]];
[_carbonTabSwipeNavigation setSelectedColor:[UIColor whiteColor]];
} else {
[self prepareControllerForEdition];
}
[_carbonTabSwipeNavigation insertIntoRootViewController:self];
self.pageViewController = _carbonTabSwipeNavigation.pageViewController;
}
- (UIViewController *)carbonTabSwipeNavigation:(CarbonTabSwipeNavigation *)carbontTabSwipeNavigation
viewControllerAtIndex:(NSUInteger)index {
_currentPageIndex = index;
// Create a new view controller and pass suitable data.
FormPageViewController *formPageViewController = [[FormPageViewController alloc] init];
formPageViewController.pageIndex = index;
formPageViewController.formPage = _sortedPages[index];
formPageViewController.managedObjectContext = self.managedObjectContext;
formPageViewController.formRecord = self.formRecord;
formPageViewController.observer = self;
formPageViewController.view.frame = CGRectMake(0,
0,
self.view.frame.size.width,
self.view.frame.size.height);
return formPageViewController;
}
#pragma mark - Button Actions (IBActions)
-(IBAction)send:(id)sender
{
_timer = [NSTimer scheduledTimerWithTimeInterval:0.001
target:self
selector:#selector(isAlertViewShowing:)
userInfo:nil
repeats:YES];
[self setButtonWithTag:self.btnBigSend.tag toHighlight:NO];
// Disabling button to avoid double submissions.
self.btnBigSend.enabled = NO;
// Show alert.
[self showAreYouReadyToSubmitFormMsg];
}
... can't paste it all
For testing only:
Subclass UIAlertView i.e. #interface MyAlertView : UIAlertView
Then replace all instances of UIAlertView from MyAlertView
i.e. MyAlertView *someAlert = [[MyAlertView alloc] init.......];
Then override
-(void)show {
[super show];
//Your breakpoint here
OR
NSLog([NSThread callStackSymbols]);
}
Check your viewcontroller that has an uialertviewdelegate.
Log your alertview.delegate
Check your super class of a viewcontroller that it doesn't call uialertviewdelegate function.
If it is an UIAlertController, check viewwillappear, viewdidappear, viewwilldisappear (super class too) and find out they don't call [alertview show]
Why you take enum for alertview ? just make instance of UIAlertView where it require's to show. you can make one method in which you can pass two string parameters alertview massage and title and method shows alertview with this title and massage.
You can catch the content of your AlertView, if it has no content at all, don't present it!
To do this check the message you are passing to the method that presents the alertView.
However, I can't seem to find your method showAreYouReadyToSubmitFormMsg.
I have a requirement in which the view contains one native UITextField and one UIWebView. The issue is if I switch the focus from UITextView to UIWebView, the keyboard window flicker(hides and then shows).
ie, I got UIKeyboardWillHideNotification and UIKeyboardDidShowNotification
But, this is not happening when I switch the other way. ies, I got only UIKeyboardDidShowNotification
Is there any way to avoid this flickering effect?
Note: I also notices if I have multiple UITextField and UIWebView, this issue is not happening with the same type of views.
I've solved this problem through the code below. Simply set webView.usesGUIFixes = YES; and it should solve your problem. The code below also lets you set a custom input view to use for the UIWebView keyboard:
UIWebView+GUIFixes.h
#import <UIKit/UIKit.h>
#interface UIWebView (GUIFixes)
/**
* #brief The custom input accessory view.
*/
#property (nonatomic, strong, readwrite) UIView* customInputAccessoryView;
/**
* #brief Wether the UIWebView will use the fixes provided by this category or not.
*/
#property (nonatomic, assign, readwrite) BOOL usesGUIFixes;
#end
UIWebView+GUIFixes.m
#import "UIWebView+GUIFixes.h"
#import <objc/runtime.h>
#implementation UIWebView (GUIFixes)
static const char* const kCustomInputAccessoryView = "kCustomInputAccessoryView";
static const char* const fixedClassName = "UIWebBrowserViewMinusAccessoryView";
static Class fixClass = Nil;
- (UIView *)browserView
{
UIScrollView *scrollView = self.scrollView;
UIView *browserView = nil;
for (UIView *subview in scrollView.subviews) {
if ([NSStringFromClass([subview class]) hasPrefix:#"UIWebBrowserView"]) {
browserView = subview;
break;
}
}
return browserView;
}
- (id)methodReturningCustomInputAccessoryView
{
UIView* view = [self performSelector:#selector(originalInputAccessoryView) withObject:nil];
if (view) {
UIView* parentWebView = self.superview;
while (parentWebView && ![parentWebView isKindOfClass:[UIWebView class]])
{
parentWebView = parentWebView.superview;
}
UIView* customInputAccessoryView = [(UIWebView*)parentWebView customInputAccessoryView];
if (customInputAccessoryView) {
view = customInputAccessoryView;
}
}
return view;
}
- (BOOL)delayedBecomeFirstResponder
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[super becomeFirstResponder];
});
return YES;
}
- (void)ensureFixedSubclassExistsOfBrowserViewClass:(Class)browserViewClass
{
if (!fixClass) {
Class newClass = objc_allocateClassPair(browserViewClass, fixedClassName, 0);
IMP oldImp = class_getMethodImplementation(browserViewClass, #selector(inputAccessoryView));
class_addMethod(newClass, #selector(originalInputAccessoryView), oldImp, "##:");
IMP newImp = [self methodForSelector:#selector(methodReturningCustomInputAccessoryView)];
class_addMethod(newClass, #selector(inputAccessoryView), newImp, "##:");
objc_registerClassPair(newClass);
IMP delayedFirstResponderImp = [self methodForSelector:#selector(delayedBecomeFirstResponder)];
Method becomeFirstResponderMethod = class_getInstanceMethod(browserViewClass, #selector(becomeFirstResponder));
method_setImplementation(becomeFirstResponderMethod, delayedFirstResponderImp);
fixClass = newClass;
}
}
- (BOOL)usesGUIFixes
{
UIView *browserView = [self browserView];
return [browserView class] == fixClass;
}
- (void)setUsesGUIFixes:(BOOL)value
{
UIView *browserView = [self browserView];
if (browserView == nil) {
return;
}
[self ensureFixedSubclassExistsOfBrowserViewClass:[browserView class]];
if (value) {
object_setClass(browserView, fixClass);
}
else {
Class normalClass = objc_getClass("UIWebBrowserView");
object_setClass(browserView, normalClass);
}
[browserView reloadInputViews];
}
- (UIView*)customInputAccessoryView
{
return objc_getAssociatedObject(self, kCustomInputAccessoryView);
}
- (void)setCustomInputAccessoryView:(UIView*)view
{
objc_setAssociatedObject(self,
kCustomInputAccessoryView,
view,
OBJC_ASSOCIATION_RETAIN);
}
#end
While this example of a Phonegap plugin works well to play videos using MPMoviePlayerController, I am searching for a similar approach to launch videos with the AVPlayer instead (still in a Phonegap Plugin)?
I need something like this but I can't figure out how to call the player and get the player view started by the plugin.
AVPlayerViewPlay is the CDVPlugin call:
// AVPlayerViewPlay.h
//
#import <Cordova/CDV.h>
#import <UIKit/UIKit.h>
#import "AVPlayerView.h"
#import "AVPlayerViewController.h"
#class AVPlayerViewController;
#interface AVPlayerViewPlay : CDVPlugin
- (void)play:(CDVInvokedUrlCommand*)command;
#end
//
// AVPlayerViewPlay.m
//
#import <Cordova/CDV.h>
#import <AVFoundation/AVFoundation.h>
#import "AVPlayerViewPlay.h"
#import "AVPlayerViewController.h"
#implementation AVPlayerViewPlay
- (void)play:(CDVInvokedUrlCommand*)command {
CDVPluginResult* pluginResult = nil;
NSString* javaScript = nil;
//!! WHAT DO I NEED TO DO HERE TO START the AVPlayerView and play the 2 videos AS A PHONEGAP PLUGIN??
//self.playbackViewController = [[AVPlayerViewController alloc] init];
//[self.playbackViewController setURL:URLWithString:movie];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:#"PLUGIN WORKS "];
javaScript = [pluginResult toSuccessCallbackString:command.callbackId];
[self writeJavascript:javaScript];
}
- (void)dealloc {
[super dealloc];
}
#end
AVPlayerView is the AVPlayer view:
//
// AVPlayerView.h
//
#import <UIKit/UIKit.h>
#class AVPlayer;
#interface AVPlayerView : UIView
#property (nonatomic, retain) AVPlayer* player;
- (void)setPlayer:(AVPlayer*)player;
- (void)setVideoFillMode:(NSString *)fillMode;
#end
//
// AVPlayerView.m
//
#import "AVPlayerView.h"
#import <AVFoundation/AVFoundation.h>
#implementation AVPlayerView
+ (Class)layerClass
{
return [AVPlayerLayer class];
}
- (AVPlayer*)player
{
return [(AVPlayerLayer*)[self layer] player];
}
- (void)setPlayer:(AVPlayer*)player
{
[(AVPlayerLayer*)[self layer] setPlayer:player];
}
- (void)setVideoFillMode:(NSString *)fillMode
{
AVPlayerLayer *playerLayer = (AVPlayerLayer*)[self layer];
playerLayer.videoGravity = fillMode;
}
#end
AVPlayerViewController is the AVPlayer controller:
//
// AVPlayerViewController.h
//
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#class AVPlayerViewController;
#interface AVPlayerViewController : UIViewController {
AVQueuePlayer *queuePlayer;
IBOutlet AVPlayerViewController *mPlaybackView;
}
#property (readwrite, retain) AVQueuePlayer *queuePlayer;
#property (nonatomic, retain) IBOutlet AVPlayerViewController *mPlaybackView;
- (void)observeValueForKeyPath:(NSString*) path ofObject:(id)object change:(NSDictionary*)change context:(void*)context;
#end
//
// AVPlayerViewController.m
//
#import "AVPlayerViewController.h"
#import "AVPlayerView.h"
static void *AVPlayerDemoPlaybackViewControllerStatusObservationContext = &AVPlayerDemoPlaybackViewControllerStatusObservationContext;
#implementation AVPlayerViewController
#synthesize mPlaybackView, queuePlayer;
- (void)dealloc
{
[queuePlayer release];
[mPlaybackView release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *secondVideoPath = [[NSBundle mainBundle] pathForResource:#"video1" ofType:#"mp4"];
NSString *firstVideoPath = [[NSBundle mainBundle] pathForResource:#"video2" ofType:#"mov"];
AVPlayerItem *firstVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:firstVideoPath]];
AVPlayerItem *secondVideoItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:secondVideoPath]];
self.queuePlayer = [AVQueuePlayer queuePlayerWithItems:[NSArray arrayWithObjects:firstVideoItem, secondVideoItem,nil]];
// I get a WARNING here that AVPlayerViewController may not respong to 'setPlayer'
[self.mPlaybackView setPlayer:self.queuePlayer];
[self.queuePlayer play];
}
#end
I have looked everywhere and I can't find any such plugin example. The code above was basically inspired from here (IOS Native). I work mostly with Javascript so I think my problem is to figure out how to replace an Interface Builder IBoutlet Play buttons by a Phonegap plugin call?
Thanks!
I am trying to code a music player for an assignment and I am stuck with trying to get the slider to update as the song is playing. I have been able to make it so that if the user were to slide the slider the song will rewind/fast-forward to that part of the song. Does anyone know why the slider doesn't continuously update as the song is playing?
Here is the .h file
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface AS3MPViewController : UIViewController <MPMediaPickerControllerDelegate>{
IBOutlet UIButton *playButton;
IBOutlet UIButton *stopButton;
IBOutlet UIButton *pauseButton;
IBOutlet UISlider *volumeSlider;
AVAudioPlayer *musicPlayer;
IBOutlet UIButton *pickSongButton;
IBOutlet UISlider *timeSlider;
NSTimer *timer;
}
#property (nonatomic, retain) UIButton *playButton;
#property (nonatomic, retain) UIButton *stopButton;
#property (nonatomic, retain) UIButton *pauseButton;
#property (nonatomic, retain) UISlider *volumeSlider;
#property (nonatomic, retain) AVAudioPlayer *musicPlayer;
#property (nonatomic, retain) UIButton *pickSongButton;
#property (nonatomic, retain) UISlider *timeSlider;
#property (nonatomic, retain) NSTimer *timer;
- (IBAction) play;
- (IBAction) stop;
- (IBAction) pause;
- (IBAction) changeVolume: (UISlider *)sender;
- (IBAction) chooseSong: (UIButton *)sender;
- (IBAction) changeTime: (UISlider *)sender;
- (void)setTimeSliderOptions;
#end
Here is the .m file
#import "AS3MPViewController.h"
#interface AS3MPViewController ()
#end
#implementation AS3MPViewController
#synthesize playButton = _playButton;
#synthesize stopButton = _stopButton;
#synthesize pauseButton = _pauseButton;
#synthesize musicPlayer = _musicPlayer;
#synthesize volumeSlider = _volumeSlider;
#synthesize pickSongButton = _pickSongButton;
#synthesize timeSlider = _timeSlider;
#synthesize timer;
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"animals001" ofType:#"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:filePath];
self.musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
[self.musicPlayer prepareToPlay];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) play {
[self.musicPlayer play];
}
- (IBAction) stop {
[self.musicPlayer stop];
self.musicPlayer.currentTime = 0;
}
- (IBAction) pause {
[self.musicPlayer pause];
}
- (IBAction) changeVolume: (UISlider *)sender {
self.musicPlayer.volume = [sender value];
}
- (IBAction)chooseSong:(UIButton *)sender {
MPMediaPickerController *mediaPicker = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio];
[mediaPicker setDelegate:self];
[mediaPicker setAllowsPickingMultipleItems:NO];
mediaPicker.prompt = NSLocalizedString(#"Add Some Songs To Play", "Prompt In Media Item Picker");
[self presentModalViewController: mediaPicker animated:YES];
}
- (void) mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
[self dismissModalViewControllerAnimated:YES];
}
- (void) mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems: (MPMediaItemCollection *)mediaItemCollection {
MPMediaItem *item = [[mediaItemCollection items] objectAtIndex:0];
NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
//AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:url];
//AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
self.timeSlider.maximumValue = [self.musicPlayer duration];
self.musicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[self.musicPlayer play];
timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(songCurrentTime) userInfo:nil repeats:YES];
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)changeTime: (UISlider *)sender {
self.musicPlayer.currentTime = self.musicPlayer.duration * sender.value;
}
- (void)songCurrentTime {
timeSlider.value = (self.musicPlayer.currentTime / self.musicPlayer.duration);
}
#end
check in the connections that you are connected the IBAction method with slider with ValueChanged
EX:
- (IBAction)changeTime: (UISlider *)sender {
check whether this method is connected with ValueChanged while IB connection
I think your problem is that you are updating the value of the slider from a thread other than the main one. Try modifying the songCurrentTime method so that it calls another one on the main thread that updates the value of the slider.
- (void)songCurrentTime {
[self performSelectorOnMainThread:#selector(songCurrentTimeMain) withObject:nil waitUntilDone:YES];
}
- (void)songCurrentTimeMain {
timeSlider.value = (self.musicPlayer.currentTime / self.musicPlayer.duration);
}
Of course to get correct results make sure the minimum value of the slider is 0 and the maximum is 1.
Best Regards
I've done something similar within this tutorial and the way I added a slider was the same as you did except for the fact that I used
musicPlayer.currentPlaybackTime
as a value to assign to the slider.
It may be that the result of (self.musicPlayer.currentTime / self.musicPlayer.duration) is an inaccurate assignment to your slider value.
When you set your slider value, the value is always less than or equal to 1 because self.musicPlayer.currentTime is always less than or equal to self.musicPlayer.duration. Instead, try this
timeSlider.value = self.musicPlayer.currentTime;
timeSlider and self.timeSlider are not the same thing. Most of the iVars that you have declared are unnecessary (and misleading) because you synthesize your properties using the underscore form of the property name. For example, you have a timeSlider property that's backed by a _timeSlider iVar, and a timeSlider iVar that's a different thing entirely.
Use self.timeSlider.value = ....
For me the issue was different i need to set initial value to slider, but it was a silly mistake that i am updating slider value before setting maximum and minimum value.
Wrong code :
float value = [self getSliderDefaultValue];
[slider setValue:value animated:NO];
slider.maximumValue = numberOfSteps;
slider.minimumValue = 0;
Working code :
slider.maximumValue = numberOfSteps;
slider.minimumValue = 0;
float value = [self getSliderDefaultValue];
[slider setValue:value animated:NO];
Hope it will help others!!!
There was transparent view over my UISlider in my case and i was overlooking in to it.
I am working on an app that will let me play different videos on the iPad remotely with an iPhone. I have been following along with apples example for a video player but I've been having some troubles. The videos play just fine and I can get it to play from a variety of videos but switching between them a few times it will crash and i get this in the debugger:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
*** First throw call stack:
(0x380da8bf 0x37c261e5 0x30acbcb5 0x30abc1f7 0x30ac3bf3 0x30c93d55 0x30c95f7b 0x380ad2dd 0x380304dd 0x380303a5 0x37e07fcd 0x31bb0743 0x25e5 0x257c)
This is the code I am using to create the player:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[self setMoviePlayerController:player];
[self installMovieNotificationObservers];
[player setContentURL:movieURL];
[player setMovieSourceType:sourceType];
[self applyUserSettingsToMoviePlayer];
[self.view addSubview:self.backgroundView];
[player.view setFrame:self.view.bounds];
[player.view setBackgroundColor = [UIColor blackColor];
[self.view addSubview:player.view];
}
And when the current movie is stopped I use:
[[self moviePlayerController] stop];
MPMoviePlayerController *player = [self moviePlayerController];
[player.view removeFromSuperview];
[self removeMovieNotificationHandlers];
[self setMoviePlayerController:nil];
Edit:
So Ive now discovered it happens every time i try and switch a video for the 11th time. weird! I'm practically pulling my hair out.
What fixed this problem for me was stopping the MPMoviePlayerController before doing the setContentURL.
MPMoviePlayerController *streamPlayer;
[streamPlayer stop];
[streamPlayer setContentURL:[NSURL URLWithString:selectedStation]];
In the implementation you have above, ARC doesn't know that the MPMoviePlayerController is finished and needs to be released.
Define MPMoviePlayerController in your .h file and make it accessible via a #property (and #synthesize).
#property (strong, nonatomic) MPMoviePlayerController * moviePlayerController;
Then take the result of your alloc & init and assign it to that. I.E.
self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
you should just keep the moviePlayerController and if you want to play another video, just use
[self.moviePlayerController setContentURL:movieURL];
then in your notification callback:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
and please do not remove the notification handler from notification center, only do this in dealloc method of your VC.
now let's add some fade when the movie play is done:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[UIView animateWithDuration:1
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
// one second to fade out the view
self.moviePlayer.view.alpha = 0.0;
}
completion:^(BOOL finished){
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
}
I had exactly the same problem.
Nothing was wrong with my and i guess with your code :)
Just a broken video file was mine problem.
Changing *.mov type to m4a for example fixed it. Maybe one or more of the files you play are corrupted?
Try to find out which files lead to crash and than if u can try to quickly forward backward the play position of one of them while playing - this should lead to crash in few tries.
This is how i found the bad files. By the way all my bad files were movies .mov made with Snapz Pro X :)
Not sure if it is the case here, but we had a lot of problems, because the MPMoviePlayer is a singleton somewhere under the hood.
What we did is, that we implemented our own MoviePlayer wrapper which can be used from UIView (actually we have exactly one subclass of UIView MoviePlayerView to show movies) and assures that only one instance of MPMoviePlayerController exists. The code goes like this (it contains some special stuff, we need to show previews/thumbs the way we want etc. you should clean up as well as some release-statements):
// MoviePlayer.h
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "Logger.h"
#class MoviePlayerView;
#interface MoviePlayer : NSObject
{
#private
MPMoviePlayerController *controller;
MoviePlayerView *currentView;
}
#property (nonatomic, readonly) MPMoviePlayerController *controller;
+(MoviePlayer *) instance;
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view;
-(void) stopMovie;
#end
// MoviePlayer.m
#import "MoviePlayer.h"
#import "MoviePlayerView.h"
#implementation MoviePlayer
#synthesize controller;
static MoviePlayer *player = nil;
#pragma mark Singleton management
+(MoviePlayer *) instance
{
#synchronized([MoviePlayer class])
{
if (player == nil)
{
player = [[super allocWithZone:NULL] init];
player->controller = [[MPMoviePlayerController alloc] init];
player->controller.shouldAutoplay = NO;
player->controller.scalingMode = MPMovieScalingModeAspectFit;
player->currentView = nil;
}
return player;
}
}
+(id) allocWithZone:(NSZone *)zone
{
return [[self instance] retain];
}
-(id) copyWithZone:(NSZone *)zone
{
return self;
}
-(id) retain
{
return self;
}
-(NSUInteger) retainCount
{
return NSUIntegerMax;
}
-(oneway void) release
{
// singleton will never be released
}
-(id) autorelease
{
return self;
}
#pragma mark MoviePlayer implementations
-(void) stopMovie
{
#synchronized(self)
{
if (controller.view.superview)
{
[controller.view removeFromSuperview];
}
if (controller.playbackState != MPMoviePlaybackStateStopped)
{
[controller pause];
[controller stop];
}
if (currentView)
{
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc removeObserver:currentView name:MPMoviePlayerLoadStateDidChangeNotification object:controller];
[ntfc removeObserver:currentView name:MPMoviePlayerPlaybackStateDidChangeNotification object:controller];
currentView = nil;
}
}
}
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view
{
#synchronized(self)
{
[self stopMovie];
currentView = view;
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc addObserver:currentView
selector:#selector(loadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:controller];
[ntfc addObserver:currentView
selector:#selector(playbackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:controller];
[controller setContentURL:movieURL];
controller.view.frame = view.bounds;
[view addSubview: controller.view];
[controller play];
}
}
#end
// MoviePlayerView.h
#import <UIKit/UIKit.h>
#import "MoviePlayer.h"
#interface MoviePlayerView : MediaView
{
NSURL *movieURL;
NSURL *thumbnailURL;
UIImageView *previewImage;
UIView *iconView;
BOOL hasPreviewImage;
}
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie;
-(void) loadStateDidChange:(NSNotification *)ntf;
-(void) playbackStateDidChange:(NSNotification *)ntf;
#end
// MoviePlayerView.m
#import "MoviePlayerView.h"
#interface MoviePlayerView()
-(void) initView;
-(void) initController;
-(void) playMovie;
-(void) setActivityIcon;
-(void) setMovieIcon:(float)alpha;
-(void) clearIcon;
-(CGPoint) centerPoint;
#end
#implementation MoviePlayerView
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie
{
self = [super initWithFrame:frame];
if (self)
{
movieURL = [movie retain];
thumbnailURL = [thumbnail retain];
[self initView];
[self initController];
hasPreviewImage = NO;
loadingFinished = YES;
}
return self;
}
-(void) dealloc
{
[iconView release];
[previewImage release];
[movieURL release];
[super dealloc];
}
-(void)initView
{
self.backgroundColor = [UIColor blackColor];
// add preview image view and icon view
previewImage = [[UIImageView alloc] initWithFrame:self.bounds];
[previewImage setContentMode:UIViewContentModeScaleAspectFit];
UIImage *img = nil;
if (thumbnailURL)
{
img = [ImageUtils loadImageFromURL:thumbnailURL];
if (img)
{
previewImage.image = img;
hasPreviewImage = YES;
}
}
[self addSubview:previewImage];
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
}
-(void)initController
{
UITapGestureRecognizer *rec = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(playMovie)];
[self addGestureRecognizer:rec];
[rec release];
}
-(void)playMovie
{
[[MoviePlayer instance] playMovie:movieURL onView:self];
[self setActivityIcon];
}
-(void) loadStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.loadState)
{
case MPMovieLoadStatePlayable:
{
[self clearIcon];
[controller setFullscreen:YES animated:YES];
break;
}
case MPMovieLoadStateStalled:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) playbackStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.playbackState)
{
case MPMoviePlaybackStatePlaying:
{
[self clearIcon];
break;
}
case MPMoviePlaybackStateStopped:
{
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
break;
}
case MPMoviePlaybackStatePaused:
{
[self setMovieIcon:0.8f];
break;
}
case MPMoviePlaybackStateInterrupted:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) setActivityIcon
{
[self clearIcon];
iconView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
iconView.center = [self centerPoint];
[self addSubview:iconView];
[iconView performSelector:#selector(startAnimating)];
}
-(void) setMovieIcon:(float)alpha
{
[self clearIcon];
iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_movie.png"]];
iconView.center = [self centerPoint];
iconView.alpha = alpha;
[self addSubview:iconView];
}
-(void) clearIcon
{
if (iconView)
{
SEL stop = #selector(stopAnimating);
if ([iconView respondsToSelector:stop])
{
[iconView performSelector:stop];
}
[iconView removeFromSuperview];
[iconView release];
iconView = nil;
}
}
-(CGPoint) centerPoint
{
return CGPointMake(roundf(self.bounds.size.width / 2.0f), roundf(self.bounds.size.height / 2.0f));
}
-(void)resize
{
for (UIView *view in [self subviews])
{
if (view == iconView)
{
iconView.center = [self centerPoint];
continue;
}
view.frame = self.bounds;
}
[self addCaptionLabel];
}
-(void) layoutSubviews
{
[super layoutSubviews];
[self resize];
}
#end
...
player = [[MPMoviePlayerController alloc] initWithContentURL: [NSURL URLWithString:...
...
but I didn't gave internet connection to phone (wi-fi) :)
I had the same problem. My solution is using prepareToPlay:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[player prepareToPlay];
//...
}
This error seems to be thrown for lots of different reasons, but the reason I found was that the MPMoviePlayerController class freaks out if you call methods in a certain order. From an IRC Channel:
"apparently if you call prepareToPlay WHILE setting source type and
NOT setting the view yet causes this crash"
So I fixed this by just making sure that I called prepareToPlay: LAST (or second to last, with the last being play:).
It is also weird because my original code worked in iOS 5.1, but this problem suddenly manifested when I started using the iOS 6.0 sdk. It is possibly a bug in the MPMoviePlayerController code, so I'm going to be filing a radar report on it, as calling prepareToPlay: before setting the view / setting the sourceFileType should not throw an exception (or at least an exception that seemingly has nothing to do with the actual error)