Note: I have looked at other questions and the answers to them are quite vague or unhelpful
I have this code in View_Controller.h
#property AVAudioPlayer *playerSaxophone;
Then I do this in the same file (in viewDidLoad):
NSURL *backgroundMusicSaxophone = [NSURL fileURLWithPath:
[[NSBundle mainBundle]
pathForResource:#"saxophone" ofType:#"wav"]];
self.playerSaxophone = [[AVAudioPlayer alloc]
initWithContentsOfURL:backgroundMusicSaxophone error:nil];
self.playerSaxophone.numberOfLoops = -1;
[self.playerSaxophone setVolume:0.5];
[self.playerSaxophone play];
In a different view controller I want to be able to stop or start this audio from playing by clicking 2 buttons. Is there any way I can do this?
Edit: I tried this in the "different" view controller .m file
//I do import ViewController.h in this file
- (IBAction)stop:(id)sender {
ViewController *viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
[viewController.playerSaxophone stop];
}
But it didn't work.
You need to call:
[playerSaxophone stop];
to make the music stop.
Problem: Another view controller won't have a reference to playerSaxophone.
The fact that you want to add code that references this from another view controller means you'll have to find a way to have the three objects - view controller A, view controller B, and your audio player - communicate.
Here are a few options:
Use the delegation pattern to have one view controller tell the other view controller to stop the player.
The same as #1, but using the notification pattern. (This is ideal if you'd have a 3rd place that controls the player).
You could move playerSaxophone to be a #property on either your App Delegate or a singleton. Then each view controller will be able to find it.
These are just a few solutions. There are others.
Additional reading:
Apple's Coordinating Efforts Between View Controllers
This Stack Overflow answer
How do I set up a simple delegate to communicate between two view controllers?
Related
I am setting up an iPad app that uses a SplitViewController. In my app delegate I have the following in didFinishLaunchingWithOptions:
UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
UINavigationController *leftNavController = [splitViewController.viewControllers objectAtIndex:0];
LeftViewController *leftViewController = (LeftViewController*)[leftNavController topViewController];
DetailViewController *detailViewController = [splitViewController.viewControllers objectAtIndex:1];
NSLog(#"Detail View Ctrl >> %#", [detailViewController class]);
When I run the app, the NSLog statement returns "UINavigationController" when DetailViewController is actually a subclass of UIViewController. However, in XCode, code completion shows all the methods that are implemented in the DetailViewController subclass. Any ideas? Thanks!
I think your DetailViewController is actually embedded inside a UINavigationController, and your fourth line is in error. Take a look instead at the topViewController for the second view controller inside your split view controller, much like you do for the LeftViewController.
The reason Xcode is continuing to suggest completion for DetailViewController methods is because you've given it that type. Code completion doesn't rely on runtime behavior (how could it?) – instead, it relies on static analysis of the code that you type. If you tell Xcode that something is a DetailViewController, it'll believe you and autocomplete based on that information.
I am learning how to program apps in ios. Anyone out there know of a recent tutorial that shows how to push a new instance of a table view controller object on top of the stack without the need to create and hookup new nib?
I found an old tutorial at iphone SDK Articles dated 3/2009. The website does not exist anymore as its domain expired. But the article, using a pre-packaged 2009 "Navigation-Based Application" in xcode, shows how to drill down a table using the same view controller rather than incorporate a new nib file. In the article, the view controller is identified as a "rootview" controller.
I have managed to modify the code to populate the data from the plist to a table cell using a UIViewController class with UITableViewDelegate and UI TableViewDataSource. This was not difficult to do. However, my problems begin when a cell is activated with the didSelectRowAtIndexPath method. The tutorial's code creates a new instance of the same table view controller class, updates the cell data and pushes it to the top of the stack to effect the "drill down." When I apply the same method to my controller class, xcode throws an error. Admittedly, my ignorance is the likely cause of the problem. That is why I am looking for help.
The tutorial code implementing the didSelectRowAtIndexPath:
//RootViewController.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//Get the children of the present item.
NSArray *Children = [dictionary objectForKey:#"Children"];
if([Children count] == 0) {
DetailViewController *dvController = [[DetailViewController alloc] initWithNibName:#"DetailView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:dvController animated:YES];
[dvController release];
} else {
//Prepare to tableview.
RootViewController *rvController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
//Increment the Current View
rvController.CurrentLevel += 1;
//Set the title;
rvController.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
[rvController release];
}
}
It is hard to say for sure since you didn't post the actual error message.
One thing that may be an issue is that you pass Children to your new controller after pushing it to the stack. Therefore, some of the ViewController's view life cycle methods have probably already been called, and if you tried to do something on Children before it was set, that may crash the application.
Change it to :
rvController.tableDataSource = Children;
[self.navigationController pushViewController:rvController animated:YES];
Something else that may help : add an exception breakpoint in Xcode and set it to catch All exceptions on throw (I believe this is the default setting).
Should it crash because of something you wrote, the application will break on the actual line posing problem, which may help you understand the issue.
If this doesn't not help, as said before, be sure to post the actual error message.
I had to instantiate a navigation controller object in the app delegate. Self.navigationController does not work unless there is a navigation controller in play. I have since updated the code to work with storyboards by utilizing the UIStoryboard class.
I feel like I am slowly climbing Mt. Everest with every little step. It's painful for a new programmer (I make my living as an attorney) but somehow I seem to be moving forward.
I'm trying to do a really simple thing - I've got a main Xib file for the whole app and another Xib file for a small view.
I want the small view (Xib called "additionalView.xib") to appear in the first Xib ("ViewController.xib").
I have succeeded to do so in the "ViewController.m" but I want more - I want to do it from "additionalView.m"
There is a method I created called "openView:" in "additionalView.m" and it looks like this:
-(IBAction)openView:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"additionalView" owner:self options:nil];
UIView *nibView = [nibObjects objectAtIndex:0];
[vc.view addSubview:nibView];
}
The method is being called and the lines are being read by the debugger - but nothing happens.
No crash - No error - No small view in bigger view.
Why is that?
I know that the last line is probably what's
screwing everything up but i don't know how to put it correctly.
Your problem is that ViewController *vc = [[ViewController alloc] init]; creates a new view controller. Because it's new, it's not the one that already exists in the view controller hierarchy that's managing the display.
Your method needs to access the existing view controller. How it does that depends on your app's structure and which object has a reference to the original controller object.
Try
[self.view addView:view.vc];
However, I'm not sure what is you view structure here. You say your -(IBAction)openView:(id)sender is in your "additionalView.m", but it is not the main view controller, correct? You need to do this in the main controller, so basically move the openView: method to your ViewController.m
And you normally need a separate view controller for each view to keep things neat and separate, so the additionalView.m should be an instance of UIViewController, which you can then create from your main view as follows:
-(IBAction)openView:(id)sender
{
AdditionalView *vc = [[AdditionalView alloc] initWithNibName:#"additionalView"];
[self.view vc.view];
}
You have options ... First you don't need to create a view controller vc if you just need the view . Create a uiview and add it .
Option 1: pass a ref to the app vc as suggested above and then :
[appVC.view addsubview:additionalView]
This will add it to main.
Use a view controller manager / ref in the app delegate that you can refer to as delegate and add your view to the current showing view.
Hope this helps
I have a rootController and 5 contentControllers, each as its own class.
I want to be able to call the next content controller from the current content controller. For example, if I'm currently showing contentController1, I want a way to show contentController2.
It'd be ideal if I could add a short method to every controllers' implementation file that passed the number of the controller to be called to the actual method that loads and shows the new controller.
For example:
#implementation ContentController1
- (int) loadNextController {
//take the 1 from ContentController1, add 1 to it, and pass it somewhere else
}
Then somewhere else (the root controller? the app delegate?) add the following method that then loads and shows the contentController based on the int sent from the (int) loadNextController method:
-(void) loadNextController: (int) nextController {
//init and show controller
}
If you could show me the code and, more importantly, where it goes, I would really appreciate it.
Trevor
It's not clear exactly how you want these view controllers to relate to each other. For example, you might want to push each one in turn onto a navigation controller's stack, so that the user always has the option of going back through the previous view controllers. Or, you might have a requirement that says that the user has to go through the five controllers in succession without the option to go back, in which case you'd probably set the window's rootViewController property to each controller when its time comes. Your decision will determine how you write the code that presents each controller's view. Read View Controller Programming Guide for details.
The design you describe above has each view controller deciding which view controller to present next. That's often appropriate, especially where the view controllers form a hierarchy. From your description, though, it seems that it might be helpful to concentrate all the knowledge about the sequence in which controllers are presented in one object. Make that object the delegate of each controller, and have that object (possibly the application delegate) determine the order of the controllers. As an (untested) example, you might add this to your app delegate:
-(void)application:(UIApplication*)app didFinishLaunchingWithOptions:(NSDictionary*)options
{
//...
self.controllers = [NSArray arrayWithObjects:
[[[ViewControllerA alloc] initWithNibName:nil bundle:nil] autorelease],
[[[ViewControllerB alloc] initWithNibName:nil bundle:nil] autorelease],
//...
[[[ViewControllerN alloc] initWithNibName:nil bundle:nil] autorelease]];
// Make self the delegate for each of the view controllers.
[self.controllers setValue:self forKey:#"delegate"];
[self.navigationController pushViewController:[self.controllers objectAtIndex:0] animated:NO];
}
-(void)viewControllerDidFinish:(UIViewController*)controller
{
NSUInteger index = [self.controllers indexOfObject:controller];
NSUInteger nextIndex = index + 1;
if (nextIndex < [self.controllers count]) {
[self.navigationController pushViewController:[self.controllers objectAtIndex:nextIndex animated:YES];
}
}
Then, whenever a view controller is ready to switch to the next controller, it can just call:
[self.delegate viewControllerDidFinish:self];
Again, the idea here is that the order of the view controllers in the array determines the order in which the controllers will be presented. I've used a nav controller here, but you don't have to. Also, you'll probably want to declare a protocol with your -viewControllerDidFinish:(UIViewController*)controller method, adopt that protocol in your app delegate (or whichever object manages the controllers), and have your controllers' delegate properties specify that protocol. That'll avoid any warnings about the delegate not implementing the method.
Any help is appreciated ! It's several days I'm fighting w/o results.
The scenario:
I and iPad application have a SplitViewController that shows 2 controllersViews (Root on the left e Detail on the right)
The Root allows a recursive navigation (tree that could be several drilldown levels) and I'm calling every time the same controller class (UITableView) pushing always in the controller stack). When the user taps a cell (left side), the detail view (right side) shows the information.
Keep in mind that the detail view controller is not always the same class: it means that I'm allocating (and releasing) programmatically several detailView controllers according the kind of information I have to display.
Here the fragment:
UIViewController <ItemGenericViewController> *newDetailViewController = [[NSClassFromString(cntrClass) alloc] initWithNibName:cntrXib bundle:nil];
//the detailViewController has been defined in the head section as ItemGenericViewController
//each detailViewController is a subclass of ItemGenericViewController
detailViewController = newDetailViewController;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, nav, nil];
self.splitViewController.viewControllers = viewControllers;
[nav release];
[viewControllers release];
[detailViewController release];
Everything is working fine until a memory warning arises.
From that moment if I try to display a new detailViewcontroller the "connection" in the SplitViewController, between the RootController and the detailController, seems vanished. The result is: nothing appear on the right part of the splitController.
In the mean time if I navigate to parent level in the root controller the situation still failing.
For your information each time I push in the stack a new RootController instance (left column) I'm releasing the same controller (to save memory as usual) and I suspect, after receiving the memory warning, iOS is trying to free itself memory and my "history" disappear and the related connection, throught the split controller, too.
Is a nightmare ;-)
Do you have any suggestion ?
Thanks
Dario
I had a similar problem to you (maybe even worse - 16 combinations of possible view switches)... But I believe i have solved it right now.
So, i believe you have used Apple's example for view switching (I have, with modifications), and if you have so, problem is that "root" splitViewController (from MainWindow.xib) get's "niled" as default behavior when memory warning. And even if you add new array of view controllers to it, it will not cause any change (and even worse, it will not show any sign of warning). And solution is to check is it nil, and if is, to reinitialize it.
here is the code, using example from above:
UIViewController <ItemGenericViewController> *newDetailViewController = [[NSClassFromString(cntrClass) alloc] initWithNibName:cntrXib bundle:nil];
//the detailViewController has been defined in the head section as ItemGenericViewController
//each detailViewController is a subclass of ItemGenericViewController
detailViewController = newDetailViewController;
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:detailViewController];
// Update the split view controller's view controllers array.
NSArray *viewControllers = [[NSArray alloc] initWithObjects:self.navigationController, nav, nil];
/**** Milos Edit ****/
if (self.splitViewController == nil) {
// I'm keeping reference in app delegate, but any way to reinitialize splitViewController is OK
self.splitViewController = delegate.splitViewController;
}
/**** end of edit ****/
self.splitViewController.viewControllers = viewControllers;
[nav release];
[viewControllers release];
[detailViewController release];
Hope it will be helpful.
Cheers
Milos