Basically I have a three view stack. In the last view I got a MPMediaPickerController that lets the user pick a song from his/her library. The song is to be played later from the first view. How can I tell the player (in the first view) what should be played?
One possibility would be to send a notification and include the MPMediaItemCollection as the object maybe?
Is this a/the correct way or do you have other smarter suggestions?
I ended up using NSNotification and attaching the MediaItemCollection as userInfo.
Use the MPMediaPickerController delegate:
in your third view controller set the MPMediaPickerController delegate to be your first view. Implement the delegate callback functions in your first view controller.
Related
I want to change to another viewcontroller in swift when the phone is locked. Is there any variable or function?
When the user locks their phone 'applicationWillResignActive' and then 'applicationDidEnterBackground' from the App Delegate get called and then when the user unlock it - 'applicationWillEnterForeground' and 'applicationDidBecomeActive' get called. You can either implement your view controller to be pushed in the App Delegate or add a notification observer in your view controller to react to any of these and push/pop/modally present the view controller you need. It all depends on your implementation - it is a bit of a broad question. Hope this helps!
So I am fairly new to objective C and iOS programming. I am learning as I program and have a question. I am trying to modify a UITextField in my FirstViewController with my Second. How do I do this?
I have a UITextField name in FirstViewController and would like to be able to do something like firstViewController.name.text = #"new name"; inside of SecondViewController with a button and have the value changed so that if I switch tabs back to the FirstViewController I see the change has been made.
Thanks in advance for any help.
1、Set the FirstViewController as the delegate of SecondViewController;
2、Use NSNotificationCenter to notify the change event.
Well to call
firstViewController.name.text = #"new name"
You just need a reference to your first view controller inside of your second view controller. There are a number of ways to pass a reference around. Could go through the app delegate.
Another option that doesn't require you to have a reference to the first view controller inside the second view controller is to use NSNotificationCenter
Then you can have your second view controller observe a notification named say #"namechange" which contains the new name string.
When the button is pressed, you post a notification to the NSNotificationCenter with the new name attached. All the info is in the link above, or just google NSNotificationCenter for more tutorials and info.
I have an app with a navigation controller as the root view. There are many views that can be pushed in.
The user has to create an account to use the app. The user can then log into this account from other devices, but only one device can be logged onto the same account at a time. So if multiple devices try to log into an account, only the latest device will be logged in and the other devices are logged off (a push is sent to the devices).
Since there are multiple views that the device could be showing before it was logged off, I call popToRootViewControllerAnimated: to get back to the root view. This is because when the user logs in the next time I only want the root view to be shown (the new account might not have access to the previously shown view).
If the user has an alert view or action sheet presented (which uses the current view as its delegate) before the push is received, the view will still be shown after the popToRootViewControllerAnimate: method is called. If the user then taps on a button for the alert view or action sheet, it will send a message to the dealloc'd view and crash the app.
An example:
myViewController is being shown to the user.
myViewController create an action sheet prompting the user for a decision.
The push is received for the device to log out.
The navigation controller pops all the views controllers and now shows myRootViewController.
Since the view controllers are popped, myViewController is now dealloc'd.
The action sheet from myViewController is still shown.
When the user selects an option form the action sheet, a message is sent to myViewController, and since it is already dealloc'd, a crash will occur.
Is there any way to prevent this?
One solution I have considered would be to keep track of all the objects that uses a specific view controller as its delegate. Then when that view controller dealloc's it will also set all the object's delegates to nil. This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list.
Any better solution (or improvement to mine) would be appreciated!
Edit: The alert view and action sheet are only examples of some objects that I would use myViewController as a delegate. I am also using a number of other classes (and third-party libraries) that implements this delegate pattern.
A few ideas:
you can encapsulate the alert/action sheet view and delegate in a single class. Then when you need an alert view, create MyAlertView instead, which will also be its own delegate and will do [self release] after the user taps a button.
make your App Delegate the only delegate for all your alert views and action sheets. App Delegate is always around while the application is running, so there won't be a problem with a released delegate.
The problem with both solutions is that if you need your application to know what happened in the alert view/action sheet, you somehow need to tell the interested class of the user's choice.
You can do that by either using delegates of your own - which would mean you're back to square one - or use notifications: when the alert view/action sheet delegate is called, it would post a notification ([[NSNotificationCenter defaultCenter] postNotificationName:NotificationName object:self userInfo:userInfo];), while the interested object would look for that notification ([[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onNotification:) name:NotificationName object:nil];) and perform whatever tasks necessary in onNotification:(NSNotification*)aNotification method.
You'll be able to agree with yourself on what type of information is passed in those notifications (I would think the button number in a NSNumber class would be enough, or perhaps pass the button text, too). And you won't have to keep track of all alert views - just don't forget to remove the observer ([[NSNotificationCenter defaultCenter] removeObserver:self name:postNotificationName object:nil];) in the views' dealloc.
Edit:
"This requires me to manually take care of every view controller when they create an object that uses itself as the delegate, since I cannot think of a way to automatically create and update this list."
Actually you probably can do this in a semi-automated way: make a singleton object with a method like
-(id)delegate:(id)delegate for:(id)forWhom
And then instead of
someThingy.delegate = self;
you'd do
someThingy.delegate = [[DelegateLocker defaultLocker] delegate:self for:someThingy];
Inside the DelegateLocker you'd have a MutableDictionary with delegate class as a key and a MutableArray of someThingies as a value. Then in your view controllers' deallocs you'd call
[[DelegateLocker defaultLocker] delegateIsDying:self];
which would go through the thingies and assign delegate = nil for each
The drawback of course is that you'll be retaining all the thingies for an indefinite period of time instead of releasing them immediately.
So the ViewController that presented the action sheet iand set itself as the delegate right? So why dont you keep a reference to the ActionSheet in the ViewController, in the dealloc method of the view controller, you can check if the action sheet is visible, if it is then set the delegate of the action sheet to nil,and dismiss it...
so
-(void)dealloc
{
if(myActionSheet && [myActionSheet visible])
{
[myActionSheet setDelegate: nil];
//dismiss
}
}
Hope that helps
If you want automated solution, I think you can make a function to iterate through Ivars of your view controller to see if any Ivar has delegate property and set it to nil.
So here is my dilemma, I have a navigation controller that controls three views. The first view has audio on it, the second view image sequences/videos and the third view will have audio.
How can I make sure that all of those things are ended (or just end them) when someone clicks the "Back" button to go to a lower numbered view?
media player classes, like audio and video, usually have "stop" methods that will do the trick. You can hook in to the viewWill/Did/Appear/Disapper methods on UIViewController to know when a view is/did become(ing) visible/invisible and stop the media playback at that time. I also like to put put "stop" method calls to media players in viewWillUnload and dealloc.
Make yourself the delegate of the UINavigationController and implement this:
- (void)navigationController:(UINavigationController *)navController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Pseudo Code Here
[audio stopPlaying];
}
Look at UINavigationControllerDelegate. It has needed methods.
I've built an app that uses a UITableView inside a UINavigationController, inside a UITabBarController. Every entry in the UITableView opens up a view that contains some basic text, buttons, but most importantly, an MPMoviePlayerController that plays audio when started. A user can click this MPMoviePlayerController and continue to browse around the rest of the app (different tabs, or moving back in the navcontroller, opening other views from the tableview) and continue to hear the audio.
I'd like the user to be able to return to the view with the active MPMoviePlayerController at any time. I understand how I would go about allowing the user to return to a certain view from any view, but I'm struggling with how to prevent that view from being reloaded when the user tries accessing the same view.
Is there any way I can save a view in memory? Or save the active MPMoviePlayerController as some type of global object, so that I can at least access that from anywhere?
I appreciate any and all help. Thanks!
I'd recommend you create a property for the MPMoviePlayerController in your app's UIApplicationDelegate (which you can then access from anywhere in the code with [UIApplication sharedApplication].delegate but you will need to cast to your UIApplicationDelegate subclass).
When you come to enter the screen which plays content, check whether your movie player property in the app delegate is nil, if it is create it, otherwise re-use it.
Don't forget to release the reference to your MPMoviePlayerController when the media stops playing, or when the media has already stopped and you get a memory warning or when your app shuts down.
The down side of this approach is it causes coupling between most of your view controllers and your app delegate. You could mitigate this with the use of a protocol however.
You should simply retain it. Like this [myView retain] and keep a pointer to it in where you need. When you want myView to appear, just add it as a subview to current visible view like[myController.view addSubview:myView].
Hope that will help, Good luck!
I've found that even adding a retain doesn't do the trick. I've actually found the best success with overriding the setView (since part of unloading the view involves calling setView:nil. I have a BOOL that gets set the FIRST time the VC loads and once thats set it will never allow setView to be called again.
- (void) setView: (UIView*) view{
NSLog(#"MainViewController: setView");
// this is our attempt to stop iOS from unloading our view.. when iOS tries to unload your view they call setView:nil.. so, no!
if(!viewDidAppear) [super setView:view];
}
A little bit of a hack, but you can override setView: in your subclass so that it never allows to set the view to nil:
-(void)setView:(UIView *)view
{
if (view == nil) return;
[super setView:view];
}