I am programming a Quiz-App. In the Menu-ViewController I added Music to the project. The musicPlayer is running well and as long as the Menu-ViewController is in front, I can also controle it (play, pause, stop, ect.). When I display another ViewController, the Music runs in the Background, like I'd like to. But if try to call the play/pause Method of the first ViewController being in the secondViewController, the music nether paused nor stoped. I don't know why! If I add other Instructions to this Method, everything's going fine. (I tried the exit(0); Method. Here is my Code:
Controller 1 .h :
#implementation MenuViewController : <....> {
... }
#property (retain) AVAudioPlayer *backgroundPlayer;
- (void) playPauseMethod;
Controller 1 .m :
#interface ...
#end
#implementation MenuViewController
# synthesize
- (void) soundChanger {
if (hintergrundPlayer.isPlaying) {
[backgroundPlayer pause];}
else if (!backgroundPlayer.isPlaying) {
[backgroundPlayer play];}}
Controller 2 .h :
#import "MenuViewController.h"
#interface QuizViewController : UIViewController{}
Controller 2 .m :
#interface ...
#end
#implementation MenuViewController
# synthesize ...
//..... musicPlayer is playing music.
- (IBAction)myMusic:(id)sender {
//first try:
[[[MenuViewController alloc] init].backgroundPlayer pause];
//second try:
[[[MenuViewController alloc] init] soundChanger];}
I'd like to control the music in every ViewController. I'm looking forward to your help.
You're creating a completely new MenuViewController in Controller2 with
[[MenuViewController alloc] init]
The best way to handle it would be to set up a protocol in controller 2 with something like
#protocol <Controller2Delegate>
-(void) playButtonPressed:(id)self;
#end
Then set up a delegate property (still in controller 2) like this:
#property (weak) id <Controller2Delegate> delegate;
Then, back in Controller 1, when you create the Controller 2, set it's delegate property, like this:
QuizViewController *controller2 = [[QuizViewController alloc] init]];
controller2.delegate = self;
And then create the playButtonPressed method somewhere in controller 1. From Controller 2, you'll do something like:
[self.delegate playButtonPressed:self];
And that will call the method in Controller 1, where you can pause the background player.
Related
i have 2 views in iPhone application. FirstViewController and MultiSelectViewController.
in FirstViewController there is a button to go to MultiSelectViewController. In MultiSelectViewController i have a tableviewcontroller to multiselect and send result back to FirstViewController with Done button
my problem is with done button. i don't know how to send data back to the FirstViewController. it has to be with dissmissviewcontroller.
this is .h file of MultiSelectViewController
#protocol MultiSelectDelegate <NSObject>
-(void) multiselectViewControllerDismissed;
#end
#interface MultiSelectViewController : UITableViewController
{
__weak id myDelegate;
}
#property(nonatomic,retain)NSArray *myData;
#property(nonatomic, retain)NSMutableArray *selectedData;
#property (nonatomic, weak) id<MultiSelectDelegate> myDelegate;
this is my done button in .m file of MultiSelectViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.allowsMultipleSelection = YES;
selectedData=[[NSMutableArray alloc] init];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(multiselectViewControllerDismissed)];
self.navigationItem.rightBarButtonItem = barButton;
}
and lastly here is my done button action:
-(void)multiselectViewControllerDismissed
{
NSLog(#"%#",selectedData);
}
i don't understand how can i send data and get back in FirstViewController
You redefine
multiselectViewControllerDismissed delegate method as
multiselectViewControllerDismissedWithData:(NSMutableArray *)dataSelected
And, in .h file of FirstViewController implement the delegate i.e.,
#interface FirstViewController: UIViewController <MultiSelectDelegate>
and in the button action of FirstViewController.m assign delegate of MultipleSelectViewController as self. ie.,
MultipleSelectViewController * msvc = [[MultipleSelectViewController alloc] init];
msvc.myDelegate = self;
and implement
-(void)multiselectViewControllerDismissedWithData:(NSMutableArray *)dataSelected
this method in FirstViewController.m
And, in the Done button action method of MultipleSelectViewController.m, call method multiselectViewControllerDismissedWithData with delegate i.e.,
[self.myDelegate multiselectViewControllerDismissedWithData:selectedData];
That's it.
You could now pass selectedData array from MultipleSelectViewController to FirstViewController
Two standard ways of passing data in obj-c:
Use references and assign manually. In your example, the first view controller passes a reference of itself to the second view controller. The second view controller assigns a designated property with the required data using the reference.
The publisher-subscriber pattern using LocalNotifications. The first view controller listens for a particular location notification, and the second view controller, before being dismissed, does a broadcast with the data.
I'd recommend the 1st approach for your example. Some sample code:
In the .h file:
#interface FirstViewController:UIViewController
#property NSMutableArray *receivedData; //property to receive selected data
#end
In the .m file:
MultiSelectViewController *msvc = [MultiSelectViewController alloc] init];
msvc.presentingViewController = self; // pass reference of 1st VC to 2nd VC
[self presentViewController:msvc animated:YES];
In MultiSelectViewController.h file:
#import "FirstViewController.h"
#interface MultiSelectViewController: UITableViewController
...
#property FirstViewController *presentingViewController;
...
#end
In MultiSelectViewController.m file:
-(void)multiselectViewControllerDismissed
{
NSLog(#"%#",selectedData);
presentingViewController.receivedData = selectedData;
}
first create your delegate method as
-(void)dismiss:(NSString *)str;
while did select get a value from based on indexpath.row
and store it as nsstring.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.valueString=[NSString stringWithFormat:#"%#",[self.arrayValue objectAtIndex:indexPath.row]];
NSLog(#"%d",indexPath.row);
}
done button click:
call your delegate method like
[self. myDelegate dismiss:"your string which you get from table"]
[[self navigationController] popViewControllerAnimated:YES];
and in your first view controller…
import the view controller class and call delegate method..
create instance for second view controller...
MultiSelectViewController *txtNext=[[MultiSelectViewController alloc]init];
txtNext. myDelegate =self;
then
-(void)dismiss:(NSString *)str
{
NSString *strng=str;
nslog("%#",strng);
}
Just started xcode 5 and xctest. How do I test that a view loads on button press. I have programatically added method that gets called when the rightBarButtonItem is clicked
action:#selector(onSettingsButton)
and in onSettingsButton
-(void) onSettingsButton{
SettingsViewController *svc = [[SettingsViewController alloc] init];
[self.navigationController pushViewController:svc animated:YES];
}
How to write xctest to ensure SettingsViewController brings up the Settings view? Thank you.
You need an interaction test — that is, a test that checks interactions between objects. In this case, you want to test that -pushViewController:animated: is called on the navigation controller with a SettingsViewController. So we want to put a mock object into self.navigationController which we can ask, "Were you called as expected?"
I'll assume a simple name for the class: MyView.
The way I'd do this by hand is to Subclass and Override navigationController. So in my test code, I'd do something like this:
#interface TestableMyView : MyView
#property (nonatomic, strong) id mockNavigationController;
#end
#implementation TestableMyView
- (UINavigationController *)navigationController
{
return mockNavigationController;
}
#end
Now instead of creating a MyView, the test will create a TestableMyView and set its mockNavigationController property.
This mock can be anything, as long as it responds to -pushViewController:animated: and records the arguments. Here's a simple example, by hand:
#interface MockNavigationController : NSObject
#property (nonatomic) int pushViewControllerCount;
#property (nonatomic, strong) UIViewController *pushedViewController;
#property (nonatomic) BOOL wasPushViewControllerAnimated;
#end
#implementation MockNavigationController
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
self.pushViewControllerCount += 1;
self.pushedViewController = viewController;
self.wasPushViewControllerAnimated = animated;
}
#end
Finally, here's a test:
- (void)testOnSettingsButton_ShouldPushSettingsViewController
{
// given
MockNavigationController *mockNav = [[MockNavigationController alloc] init];
TestableMyView *sut = [[TestableMyView alloc] init];
sut.mockNavigationController = mockNav;
// when
[sut onSettingsButton];
// then
XCTAssertEquals(1, mockNav.pushViewControllerCount);
XCTAssertTrue([mockNav.pushedViewController isKindOfClass:[SettingsViewController class]]);
}
These things can be simplified by using mock object frameworks such as OCMock, OCMockito, or Kiwi's mocking. But I think it helps to start by hand first, so that you understand the concepts. Then choose the tools that help. And if you know how to do it by hand, you'll never say, "Mocking framework X doesn't do what I need! I'm stuck!"
Found one way. Maybe there are others ..
- (void)testSettingsViewShowsWhenSettingsButtonIsClicked{
[self.tipViewController onSettingsButton];
id temp = self.tipViewController.navigationController.visibleViewController;
XCTAssertEqual([temp class], [SettingsViewController class], #"Current controller should be Settings view controller");
}
First call the onSettingsButton, which is the same as clicking the button, but not really. Maybe it's okay for this simple test case? How to simulate the actual press?
Then get the current view from the tipviewcontoller which is the rootview of the app and check that it is a SettingsViewController.
I have an initial ViewController, lets call it HomeViewController, that has a button which calls a modal view controller, lets call it ModalViewController. Inside that ModalViewController I have a table view with two sections. If you click on any cell from section 0 it sends information back to HomeViewController (this part I have working with Protocol). If you click on any of the cells from section 1 it pushes to another view controller with options, lets call it OptionsViewController. Here is where it gets tricky for me. If you click any of those options, dismiss OptionsViewController and close ModalViewcontroller and send that information to HomeViewController, just like ModalViewController to HomeViewController. I have tried to set up a protocol similar to the one in ModalViewController but it is never called.
OptionsViewController protocol & .h file
#protocol OptionsViewControllerDelegate <NSObject>
#optional
-(void) optionsInfo:(NSArray *)optionsViewArray;
#end
#interface OptionsViewController : UITableViewController
#property (retain) id delegate;
#property (nonatomic, strong) NSArray *sendArray;
#end
OptionsViewController.m where it's called to pop off the stack.
{
[self dismissOptionsView];
}
-(void) viewWillDisappear:(BOOL)animated
{
NSLog(#"Send Array: %#", self.sendArray);
[[self delegate] optionsInfo:sendArray];
}
-(void)dismissOptionsView
{
[self.navigationController popViewControllerAnimated:YES];
}
Inside ModalViewController.h
#protocol ModalViewControllerDelegate <NSObject>
#optional
-(void) sendInformation:(NSArray *)infoArray;
#end
#interface ModalViewController : UITableViewController <ConditionsViewControllerDelegate, UISearchBarDelegate>
{
UISearchBar *searchDrugBar;
}
#property (retain) id delegate;
#property (nonatomic, strong) IBOutlet UISearchBar *searchDrugBar;
#property (nonatomic, strong) NSArray *infoArray;
#end
ModalViewController.m where OptionsInfo is supposed to come in.
-(void) optionsInfo:(NSArray *)optionsViewArray
{
//IT NEVER REACHES HERE :(
NSLog(#"HERE");
self.infoArray = optionsViewArray;
NSLog(#"Finished");
[self dismissViewControllerAnimated:YES completion:nil];
}
Has any one has done something similar like this or knows the solution to this? Any information, link, guidance and etc. to the right direction will be appreciated. Thanks in advance.
You need to set the delegate in your OptionsViewController below:-
In your OptionsViewController.m Include below line on your method
[self setDelegate:ModalViewController];
I ultimately want to write an iOS app incorporating ALAssetsLibrary, but as a first step toward understanding delegation, I'm trying to pass a simple message between two view controllers. For some reason, I can't seem to get the message to pass. In particular, the delegate object (derpy) doesn't appear to exist (if(self.derpy) returns NO)).
I asked the same question on the Apple forums and was told that I should be using segues and setting properties / calling methods using self.child instead, but that seems strange. If I were to pass messages using the parent / child properties, would I still be able to create my views in Interface Builder? Once I have my two views set up, say inside a UINavigationController, I'm not sure how to actually "wire them up" so I can pass messages between them. Sorry if the question is overly broad.
Here's the controller I'm declaring the protocol in (called PickerViewController):
Interface:
#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#protocol DerpDelegate <NSObject>
#required
- (void) test;
#end
#interface PickerViewController : UIViewController
#property (nonatomic, assign) id<DerpDelegate> derpy;
#end
Implementation:
#import "PickerViewController.h"
#interface PickerViewController ()
#end
#implementation PickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.derpy) { // If the delegate object exists
[self.derpy test]; // send it this message
} else {
NSLog(#"Still not working."); // This always returns (i.e., self.derpy doesn't exist)
}
}
Delegate controller (MainViewController) interface:
#import <UIKit/UIKit.h>
#import "PickerViewController.h"
#interface MainViewController : UIViewController <DerpDelegate> // public promise to implement delegate methods
#property (strong, nonatomic) PickerViewController *picker;
- (void) test;
#end
And lastly, the delegate controller (MainViewController) implementation:
#import "MainViewController.h"
#import "PickerViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
// Here's that method I promised I'd implement
- (void) test{
NSLog(#"Test worked."); // This never gets called
}
- (void)viewDidLoad {
[super viewDidLoad];
self.picker.derpy = self;
//lazy instantiation
- (PickerViewController *) picker{
if(!_picker) _picker = [[PickerViewController alloc]init];
return _picker;
}
EDIT: Many thanks to rydgaze for pointing me in the right direction with self.picker.derpy = self, but for some reason, things still aren't working properly. Importantly, once that property has been set, if(self.picker.derpy) returns YES from MainViewController. But if(self.derpy) is still returning NO when called from inside the PickerViewController's viewDidLoad. How can the property exist and not exist at the same time?
You need to be sure that you're setting the delegate on the instance of the view controller that you put on screen. If you're using a navigation controller and segues to go between MainViewController and PickerViewController, then you should set the delegate in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.picker = (PickerViewController *)segue.destinationViewController;
self.picker.derpy = self;
}
You need to populate the delegate first.
Basically, your MainViewController shoudl at somepoint do a
picker.derpy = self;
Then when the delegate fires in PickerViewController, the callback will happen.
Edit:
A good practice is to do something like in PickerViewController
#property (nonatomic, assign) id<DerpDelegate > derpy;
and in your MainViewController indicate that you will implement the delegate
#interface MainViewController : UIViewController<DerpDelegate>
Eventually in your implementation of MainViewController
You will have something like
picker = [[PickerViewController alloc]init];
picker.derpy = self;
[picker doYourThing];
Once picker is all done, it may want to return results using the delegate.
I´m having problems declarating my own delegate. Well...thats not exactly true: i have it declarated and, when i build the project, the compiler reports no issues. I declarated it in this way:
I made a file (enviarDatos.h) for declare the protocol:
#protocol enviarDatos <NSObject>
- (void)addItemViewController:(NSMutableArray *)item;
#end
In the Vista2.h (ViewController) file I imported the file enviarDatos.h and declared a property:
#property (nonatomic, weak) id <enviarDatos> delegare;
In the Vista2.m (ViewController) file I use the protocol method:
#interface ViewController : UIViewController <enviarDatos> {
And, finally, in the ViewController.m file I implement the delegates method:
- (void)addItemViewController:(NSMutableArray *)ar {
origen = ar;
}
Does anyone see something wrong? the code of the last function its never executing.
Thanks for your help.
EDIT:
What i need is to change an array in ViewController from Vista2 (another viewcontroller)
Then create delegate property in next view(child view) & set it to self in parent view while pushing or showing child view.
ParentView.m
1.Implement protocol methods
- (void)addItemViewController:(NSMutableArray *)ar
{
origen = ar;
}
2.While showing child view
ChildViewController *child = [[ChildViewController alloc] init];
child.delegate = self;
//present child view
ChildView.h
#property (nonatomic, weak) id <enviarDatos> delegare;
ChildView.m
-(void) anyMethod
{
if([self.delegate respondsToSelector:#selector(addItemViewController:)])
{
[self.delegate addItemViewController:mutableArray];
}
}
Ah, it looks like you are declaring the delegate property in the wrong place.
You should declare the property delegate in enviarDatos.h.
#property (nonatomic, weak) id <enviarDatos> delegate;
Then in Vista2.m you will do something like this...
EnviarDatos *myObject = [[EnviarDatos alloc] init];
myObject.delegate = self;
This then sets up the EnviarDatos object and assigns the Vista2 object as the delegate.
Now, in EnviarDatos.m you can run...
[self.delegate addItemViewController:someObjectArray];
And this will then run that code in the Vista2 object.
Delegates are used for calling back to objects that create them (or some other objects). If you create an object and then want to run a method in it then you won't need a delegate.
Can you say at what condition addItemViewController is invoked?
You seem to be on the right track, but are you sure you are setting the delegate as
[yourObject setDelegate: self];
Have you tried debugging it? Does the debugger pause at addItemViewController if you set a breakpoint there? Can you confirm the delegate is not null inside the method? I may post some code but your seems to be right except for the assigning of delegate, I think you should check it.