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
Related
I have a uiwebview in my ios program and it is connected to a variable inside the ViewController interface
#interface ViewController() <UIWebViewDelegate>
#property(weak, nonatomic) IBOutlet UIWebView *uiwebview;
#end
I want to call functions on this uiwebview from outside the viewcontroller
For example, I can control the webview just fine if it is used between
#implementation ViewController
.
.
.
#end
But I want to use it somewhere outside. For example, I want to do something like this
[uiwebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
from outside #implementation ViewController
What are the options to achieve this?
But I want to use it somewhere outside. For example, I want to do something like this
[uiwebview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"about:blank"]]];
from outside #implementation ViewController
Stop wanting that. An IBOutlet should be private to its own view controller. If you want other view controllers to be able to do something to that web view, then arm your ViewController with public methods that they can call.
I think I understand what you are trying to do, I have an app that has various tabs some of which are web view. When my app starts I get it to pre-load the web views so that when the tabs are selected they are already loaded. Its important the the other tabs are all singleton classes so that there is only one instance of them.
In my main ViewController viewDidLoad I have this ..
//Pre load calculators and status page
EvolutionViewController *evoCalc = [EvolutionViewController sharedInstance];
[evoCalc.view layoutSubviews];
IVCalcViewController *ivCalc = [IVCalcViewController sharedInstance];
[ivCalc.view layoutSubviews];
StatusViewController *serverStatus = [StatusViewController sharedInstance];
[serverStatus.view layoutSubviews];
Those instantiate the classes for the other tabs which also loads up the web views associated with those classes. All the classes are similar this is the .h
#import <UIKit/UIKit.h>
#interface EvolutionViewController : UIViewController <UIWebViewDelegate>
+ (EvolutionViewController *) sharedInstance;
#property (nonatomic, retain) IBOutlet UIWebView *webView;
#end
And the .m
#import "EvolutionViewController.h"
#interface EvolutionViewController ()
#end
#implementation EvolutionViewController
#synthesize webView;
#pragma mark - Singleton Methods
static EvolutionViewController *_sharedInstance;
- (id) init
{
if (self = [super init])
{
// custom initialization
}
return self;
}
+ (EvolutionViewController *) sharedInstance
{
return _sharedInstance;
}
-(id)initWithCoder:(NSCoder *)decoder {
if (!_sharedInstance) {
if (self = [super initWithCoder:decoder]) {
_sharedInstance = self;
}
}
return _sharedInstance;
}
#pragma mark - View Controller Methods
-(void)viewWillLayoutSubviews {
self.webView = [[UIWebView alloc] init];
}
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [[NSBundle mainBundle] URLForResource: #"evo_calc" withExtension:#"html"];
NSURLRequest*request=[NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
#end
Mine uses local html resources but works the same with a proper URL as well.
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.
I have been creating a cocoa static library in which I have a public nsobject file where I created a custom delegate. In the app I imported the nsobject file and implemented the delegate but the delegate is not getting called... the static library name is glamApi.
the SKUIDPasser.h file of the NSObject in the library
#import <Foundation/Foundation.h>
#protocol SubClassDelegate <NSObject>
#required
- (void)MethodNameToCallBack:(NSString *)s;
#end
#interface SKUIDPasser : NSObject
-(void)getSKUIDsFromCart:(NSString *)SKUIDs;
#property (nonatomic, weak) id <SubClassDelegate> delegatePasser;
#end
and the SKUIDPasser.m file
#import "SKUIDPasser.h"
#implementation SKUIDPasser
#synthesize delegatePasser;
-(void)getSKUIDsFromCart:(NSString *)SKUIDs{
NSLog(#"getSKUIDsFromCart %#",SKUIDs);
[delegatePasser MethodNameToCallBack:SKUIDs];
}
#end
And the method is called from a Viewcontroller in static library
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
[pass getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}
The Viewcontroller which the custom delegate has to be implemented Viewcontroller.h
#import <glamAPI/SKUIDPasser.h>
#interface ViewController : UIViewController<SubClassDelegate>{
SKUIDPasser *sk;
}
Viewcontroller.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
I didn't get any error but the method is not calling..Please help me to resolve this
The very first thing you need to understand is that each instance object of a class is entirely different entity and maintains it's state separately.
In you case your have created an object of your static library in viewDidLoad: and set the delegate accordingly, but when you are making the call to method getSKUIDsFromCart, you are using a different instance for which you never set the delegate property. That's why there was no callback.
To solve this, you can set the delegate in method CartShowEvent: before making the call, something like this
SKUIDPasser *pass = [[SKUIDPasser alloc] init];
pass.delegatePasser = self;
[pass getSKUIDsFromCart:#"sssss"];
However i would suggest that you should use the instance variable of library which you already created in viewDidLoad:
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//No need to create another object.
//SKUIDPasser *pass = [[SKUIDPasser alloc] init];
//Use the previously created instance object
[sk getSKUIDsFromCart:#"sssss"];
}
else {
[Utilities alert:#"No products to display !!!"];
}
}
The SKUIDPasser object that you are calling within (IBAction)CartShowEvent:(id)sender and the SKUIDPasser object that you are setting the delegate are NOT the same.
Just for a test, try calling the method [sk getSKUIDsFromCart:#"sssss"]; just after you set the delegate and you will see that it will be called because this instance has the delegate set correctly:
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
[sk getSKUIDsFromCart:#"sssss"];
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Update
I updated my answer to help you call the trigger from the static library
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
sk = [[SKUIDPasser alloc] init];
sk.delegatePasser = self;
/*
You now can pass this variable to the static library to get called
from there ...
example:
viewControllerOnStaticLibrary.passer = sk;
*/
NSLog(#"sk.delegatePasser %#",sk.delegatePasser);
}
- (void)MethodNameToCallBack:(NSString *)s
{
NSLog(#"MethodNameToCallBack %#",s);
}
Viewcontroller_in_static_library.h
#property (nonatomic, strong) SKUIDPasser *passer;
Viewcontroller_in_static_library.m
- (IBAction)CartShowEvent:(id)sender {
if (![cartBadge isHidden]) {
buyClicked = TRUE;
[self loadCart];
[self showCartItemsAll];
self.cartView.frame = self.view.bounds;
[self.view addSubview:self.cartView];
//now you are calling the same instance
[self.passer getSKUIDsFromCart:#"sssss"];
} else {
[Utilities alert:#"No products to display !!!"];
}
}
This is my first question here. I'm trying to make an app that will work with Core Audio. I found this framework http://theamazingaudioengine.com/ that I'm trying to use and so far I managed to do the first thing in the documentation, which is to play a file. However, by custom initializing the UIViewController in app's delegate, I lose all its content and the view controller comes black with no other elements.
My UIViewController only has one button, which I wanted to use to start the playback of the file, but since I have no access to it, at the moment, the file starts playing when the project builds.
Any idea what am I doing wrong?
This is my appDelegate:
#implementation SoundCheckAppDelegate
#synthesize window = _window;
#synthesize audioController = _audioController;
#synthesize viewController = _viewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Create an instance of the audio controller, set it up and start it running
self.audioController = [[[AEAudioController alloc] initWithAudioDescription:[AEAudioController nonInterleaved16BitStereoAudioDescription] inputEnabled:YES] autorelease];
_audioController.preferredBufferDuration = 0.005;
[_audioController start:NULL];
// Create and display view controller
self.viewController = [[SoundCheckViewController alloc] initWithAudioController:_audioController];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
#end
And my UIViewController:
#interface SoundCheckViewController ()
#property (nonatomic, strong) AEAudioController *audioController;
#property (nonatomic, strong) AEAudioFilePlayer *loop;
#end
#implementation SoundCheckViewController
- (id)initWithAudioController:(AEAudioController*)audioController {
self.audioController = audioController;
NSError *error;
NSURL *file = [[NSBundle mainBundle] URLForResource:#"Southern Rock Drums" withExtension:#"m4a"];
self.loop = [AEAudioFilePlayer audioFilePlayerWithURL:file
audioController:_audioController
error:&error];
if(error)
NSLog(#"couldn't start loop");
_loop.removeUponFinish = YES;
_loop.loop = YES;
_loop.completionBlock = ^{
self.loop = nil;
};
[_audioController addChannels:[NSArray arrayWithObject:_loop]];
return self;
}
#end
Since you're using a storyboard, you should take all that code out of the app delegate. The storyboard automatically instantiates your initial controller and puts it on screen. By alloc init'ing one, you're just creating another one that doesn't have any custom view.
To add your audio controller, you should add code in the viewDidLoad method of SoundCheckViewController that does that, rather than in an init method. This would be the usual way to do this, but I'm not sure what is possible with the framework you're using.
I think you should initialize your view controller first.
- (id)initWithAudioController:(AEAudioController*)audioController {
// THIS LINE IS MISSING IN YOUR CODE
self = [super initWithNibName:#"SoundCheckViewController" bundle:nil];
if ( self ) {
self.audioController = audioController;
...
}
return self;
}
So I got this for loop in a function, but it never gets entered,
for (Window *window in _app.windows) {
NSLog(#"test.");
}
I'm a beginner so where do I start to debug this and see where it goes wrong?
EDIT
This is in another class
(its in a function (loadApp) that I call in my ViewController, like this: self.app = [MyClass loadApp]; , the above code is also in my ViewController.
Window *window = [[Window alloc] initWithName:title subtitle:subtitle number:number ident:ident type:type chapternumber:chapternumber icon:icon text:text img:img question:question answerFormat:answerFormat answerLength:answerLength tip1:tip1 tip2:tip2 tip3:tip3 tip1Answer:tip1Answer tip2Answer:tip2Answer tip3Answer:tip3Answer];
[app.windows addObject:window];
}
return app;
Try the following
if(!_app) {
NSLog(#"app is nil");
}
else if(!_app.windows) {
NSLog(#"windows is nil");
}
else {
NSLog(#"there are %d windows", [_app.windows count]);
}
I suspect you'll see there are 0 windows
You have to make sure you are accessing the same variable. That is the gist of all the other comments and answers you are getting. It needs to be setup something like this. Keep in mind, your app may not be setup exactly like this. This is just a general structure to follow:
//myViewController.h
#import "WindowClass.h"
#import "AppClass.h"
#property (strong, nonatomic) AppClass *app;
//myViewController.m
#import "myViewController.h"
#synthesize app;
(id)init....{
//...init code here
//Synthesized objects must be initialized before they are accessed!
self.app = [[AppClass alloc] init];
return self;
}
(void)loadApp {
WindowClass *aWindow = [[WindowClass alloc] init];
[self.app.windowArray addObject:aWindow];
return;
}
(void)loopFunction {
for (WindowClass *window in self.app.windowArray) {
NSLog(#"test.");
}
return;
}
//AppClass.h
#property (strong, nonatomic) NSArray *windowArray;
//AppClass.m
#import "AppClass.h"
#synthesize windowArray;