how to do the same button for three screens? - ios

I have some trouble. So i have AvPlayer and UIButton with play/stop.
Also: I have three UiViewControllers. I need that when I click on the first button on the first UIVIewController on the second controller, the third controller button is pressed also, respectively, on the contrary, too. How its make? Any proposition?
It's simple code - push on button - play URL Stream and also when push again stop music.
-(IBAction)playRadioButton:(id)sender
{
if(clicked == 0) {
clicked = 1;
NSLog(#"Play");
NSString *urlAddress = #"http://URLRADIOSTREAM";
NSURL *urlStream = [NSURL URLWithString:urlAddress];
myplayer = [[AVPlayer alloc] initWithURL:urlStream];
[myplayer play];
[playRadioButton setTitle:#"Pause" forState:UIControlStateNormal];
}
else
{
NSLog(#"Stop");
[myplayer release];
clicked = 0;
[playRadioButton setTitle:#"Play" forState:UIControlStateNormal];
}
}

If you have several controllers that you need to notify about an event on another controller you can use NSNotificationCenter
Eg. in one controller in ViewDidLoad
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playBtnClicked:)
name:#"BTN_CLICKED"
object:nil];
Also in the same controller define the selector e.g.
-(void)playBtnClicked:(NSNotification *)pNotification
{
// do something
}
In the other controller trigger it by using
[[NSNotificationCenter defaultCenter]
postNotificationName:#"BTN_CLICKED" object:nil];

If you don't want to use nsnotifications then use protocol and notify other viewcontrollers by using delegates

First of all, are the 3 view controllers allocated and initialized at once? If not, I recommend you set a property on your AppDelegate class, like this:
#interface AppDelegate
#property (nonatomic, assign) BOOL commonButtonPressed;
// All your code here
#end
And you can set this property like this:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.commonButtonPressed = YES; // or NO;
Then, from your UIViewController classes:
- (void)viewWillAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.commonButtonPressed) {
// Logic of what happens to the button goes here.
}
}
Another way of doing this, without touching your AppDelegate class is using NSUserDefaults, like this:
[[NSUserDefaults standardDefaults] setBool:(<YES or NO>) forKey:#"commonButtonPressed"];
[[NSUserDefaults standardDefaults] synchronize];
You can read back the value like this:
BOOL buttonPressed = [[NSUserDefaults standardDefaults] boolForKey:#"commonButtonPressed"];

Related

rootViewController instance method only works when app enters foreground

I have a method on my rootViewController that disables horizontal scrolling within my app. When the user opens a search form within one page, I want the horizontal scrolling to be disabled. This is on the [search] page, like below:
[settings]-[search]-[people]-[chat]
The method is working properly, but only when I close out the app after first launch and reopen.
Here is the method on my rootViewController:
// .h
-(void)setScrollDisabled;
// .m
- (void)setScrollDisabled {
_mainScrollView.scrollEnabled = NO;
}
I call it when the searchBar is active in my searchViewController:
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
...
[self disableHorizontalScroll];
}
-(void)disableHorizontalScroll {
TREAppDelegate *appDelegate = (TREAppDelegate *)[[UIApplication sharedApplication] delegate];
[[appDelegate rootViewController] setScrollDisabled];
}
I know that it does work, I just want it to work when the app launches in addition to when it enters the foreground. How do I make sure the same result is achieved in all cases?
Figured it out. Needed a notification such as the following in my viewController:
[[NSNotificationCenter defaultCenter] postNotificationName:TRESearchControllerOpenedNotification object:nil userInfo:#{TRESearchControllerSearchIsActive : #(YES)}];
and then in my rootViewController:
[[NSNotificationCenter defaultCenter] addObserverForName:TREActivityControllerOpenedNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
if ([note userInfo][TRESearchControllerSearchIsActive]) {
BOOL searchIsActive = [[note userInfo][TRESearchControllerSearchIsActive] boolValue];
if (searchIsActive) {
[self setScrollDisabled];
}
}
}];
along with the proper declarations...

UINavigationController pushViewController pushes controller then "automatically" dismiss it

In my HomeViewController's viewDidAppear method, I have the following code:
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL didRunBefore = [defaults boolForKey:#"didRunBefore"];
if (!didRunBefore) {
// check to see if children already exist (previous user)
NSArray *children = [CoreDataHelper getObjectsForEntity:NSStringFromClass([Child class]) withSortKey:#"name" andSortAscending:YES andContext:self.managedObjectContext];
if (children.count == 0) {
// send user to create fist child
UIStoryboard *storyboard = self.storyboard;
ChildEditTableViewController *editController = [storyboard instantiateViewControllerWithIdentifier:#"ChildEditControllerID"];
NSManagedObjectContext *newContext = [[NSManagedObjectContext alloc] init];
newContext.parentContext = self.managedObjectContext;
editController.managedObjectContext = newContext;
[self.navigationController pushViewController:editController animated:NO];
}
}
}
Here's the code from ViewDidLoad in ChildEditTableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"Child Edit controller loaded");
self.availablePicker.delegate = self;
self.bankedPicker.delegate = self;
self.carryOverCellIsShowing = NO;
self.isNewChild = self.child == nil;
self.imageButton.layer.cornerRadius = self.imageButton.frame.size.width/2;
self.imageButton.layer.masksToBounds = YES;
[[self.imageButton imageView] setContentMode: UIViewContentModeScaleAspectFill];
if (self.isNewChild) {
// check to see if it's user's first time running app
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL didRunBefore = [defaults boolForKey:#"didRunBefore"];
if (!didRunBefore) {
// hide Home back button
[self.navigationItem setHidesBackButton:YES];
// update didRunBefore to yes
[defaults setBool:YES forKey:#"didRunBefore"];
[defaults synchronize];
}
self.child = [NSEntityDescription insertNewObjectForEntityForName:#"Child" inManagedObjectContext:self.managedObjectContext];
self.title = NSLocalizedString(#"Add New", #"Add New Title");
}
else {
if (self.child.profileImage != nil) {
[self.imageButton setImage:[UIImage squaredImageFromImage:[UIImage imageWithData:self.child.profileImage] scaledToSize:self.imageButton.frame.size.height] forState:UIControlStateNormal];
}
self.name.text = self.child.name;
self.autoBankSwitch.on = [self.child.autoBank boolValue];
self.carryOverSwitch.on = ![self.child.resetDailyTotal boolValue];
[self setCarryOverSwitchVisibility:self.autoBankSwitch];
}
// This will remove extra separators from tableview
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
}
That code works fine as far as I can see the ChildEditTableViewController load on the screen, but then it automatically pops back to the home controller. I've checked the code in the child controller and the only time I pop the controller is when the user hits a button.
Here's the Save IBAction where I pop the controller:
- (IBAction)save:(UIBarButtonItem *)sender {
[self saveToDB:sender];
[self.navigationController popViewControllerAnimated:YES];
}
If I use self.navigationController setViewControllers instead, this does not happen and ChildEditTableViewController stays loaded on the screen, but clicking the Save button (which pops the view controller) doesn't do anything.
Any ideas? (Thanks!)
**** EDIT *****
I noticed it was working fine in iOS 7.1 and 7.03. The only difference from an UI perspective was this piece of code below:
// enable handling of push notifications
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
// use registerUserNotificationSettings
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
|UIRemoteNotificationTypeSound
|UIRemoteNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
} else {
// use registerForRemoteNotifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
}
In iOS 8 I was getting a prompt to allow notifications on the simulator (something that doesn't work on the sim in prior versions). After I clicked ok is when the EditChild controller would get popped. So I commented out that code in the app delegate and the controller stays loaded just like in iOS 7.
****** EDIT ******
Below is the ApplicationDidBecomeActive code
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"%s", __PRETTY_FUNCTION__);
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
// move user to home screen so app is locked each time they open it (but not on first use)
SWRevealViewController* revealController = (SWRevealViewController*)self.window.rootViewController;
UINavigationController *nav = (UINavigationController *)revealController.frontViewController;
[nav popToRootViewControllerAnimated:YES];
}
So this is the culprit. This code is getting called again right after the user clicks Accept on the notifications registration alert, for some crazy reason.
I am thinking that the the callbacks in your application delegate are doing something to your view/controller hierarchy. I would add some break points in your application delegate methods applicationWillResignActive:, applicationDidBecomeActive: and see if they are doing anything.

MPMoviePlayerController - detect pressing Next/Prev buttons

I'm using MPMoviePlayerController and I need to detect pressing Next/Prev buttons. I tried several things, none of which seem to works.
Here is what I tried:
remote control events
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
-(void) viewWillDisappear:(BOOL)animated
{
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
-(BOOL)canBecomeFirstResponder
{
return YES;
}
-(void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent
{
// stuff
}
The problem is remoteControlReceivedWithEvent method is never called. I've read that this will not work in iOS version higher than 6 - I'm working on iOS 7
notifications
I tried using MPMoviePlayerPlaybackStateDidChangeNotification and check against MPMoviePlaybackStateSeekingForward or MPMoviePlaybackStateSeekingBackward - unfortunatelly, these playback state are set when dragging the playback bar, not when pressing Next/Prev buttons.
Any ideas?
Sorry I donĀ“t understand your problem very well, but if you want use the controls out your App in the Control Center, you can use:
// You need cath the singleton
MPRemoteCommandCenter *myRemote = [MPRemoteCommandCenter sharedCommandCenter];
//And add the selector you can fire depends on the button, a couple of examples:
[myRemote.playCommand addTarget:self action:#selector(myPlayMethods)];
[myRemote.nextTrackCommand addTarget:self action:#selector(myNextTrackMethods)];
try registering for event in :
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Turn on remote control event delivery
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Set itself as the first responder
[self becomeFirstResponder];
}
Also Don't set kAudioSessionProperty_OverrideCategoryMixWithOthers property
Have you tried MPMoviePlayerNowPlayingMovieDidChangeNotification?
If this does not work then i would suggest moving to a lower level API i.e. AVPlayer. It provides fine grained control over all the actions while video playing and otherwise.
You need to register to handle a notification for moviePlayerLoadStateChanged. When you press the next/prev buttons moviePlayerLoadStateChanged will be called and the loadState will be MPMovieLoadStateUnknown
-(void)registerMyStuff {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayerLoadStateChanged:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:self.mpc];
}
- (void)moviePlayerLoadStateChanged:(NSNotification *)notification
{
MPMoviePlayerController *moviePlayer = notification.object;
MPMovieLoadState loadState = moviePlayer.loadState;
if(loadState == MPMovieLoadStateUnknown)
{
// this is where the next/prev buttons notify
// there is no video in this state so load one
// just reload the default movie
NSLog(#"MPMovieLoadStateUnknown");
self.mpc.contentURL = self.fileURL;
[self.mpc prepareToPlay];
return;
}
else if(loadState & MPMovieLoadStatePlayable)
{
NSLog(#"MPMovieLoadStatePlayable");
}
else if(loadState & MPMovieLoadStatePlaythroughOK)
{
NSLog(#"MPMovieLoadStatePlaythroughOK");
} else if(loadState & MPMovieLoadStateStalled)
{
NSLog(#"MPMovieLoadStateStalled");
}
}
You change MPMoviePlayerController overlayView just like change UIImagePickerController overlayView to implement the function you need.
MPMoviePlayerController *moviePlayer = [[MPMoviePlayerController alloc]
initWithContentURL:someUrl];
moviePlayer.movieControlMode = MPMovieControlModeHidden;
[moviePlayer play];
NSArray *windows = [[UIApplication sharedApplication] windows];
if ([windows count] > 1) {
UIWindow *moviePlayerWindow = [[UIApplication sharedApplication] keyWindow];
[moviePlayerWindow addSubview:yourCustomOverlayView];
}

NSUSer defaults and swiping the application off ios

All,
I just cannot find an answer to this question. The settings View Controller needs to shown once on startup ONLY. So when you download the app from the App Store / test flight.
I have it correct, so it runs it first, thats fine.
when you have finished with the settings page it goes to the main page and when you move the app to the background it carries on from where it left off. thats fine.. But... When you swipe the app away by double pressing the home button and pushing the app up to remove (ios7) it goes back to the settings screen again but it should carry on from where it left off.
So in my App Delegate, I have :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *savedValue = [[NSUserDefaults standardUserDefaults] stringForKey:#"SettingsShown"];
NSLog(#"%#", savedValue);
Reachability *reachability = [Reachability reachabilityWithHostname:#"www.outtonightapp.com"];
[reachability startNotifier];
NSUserDefaults *settingsscreen = [NSUserDefaults standardUserDefaults];
[settingsscreen registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES],#"firstTime", nil]];
//BOOL firstTime = [settingsscreen boolForKey:#"firstTime"];
BOOL firstTime = [settingsscreen boolForKey:#"SettingsShown"];
if (!firstTime) {
//if ( firstTime==YES) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"SettingsShown"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"SetUpNav"];
}
else
{
return YES;
}
This did work until I had to recreate my settings VC.. any advice would be great.
In similar situation I used delegation pattern.
Assume you have your initial view controller initialVC. And special settings view controller (SetUpNav) which is meant to be run only first time when defaults is not set. Then you could do the following:
You define SetUpNavdelegate protocol in SetUpNavViewController.h and property "initiator" conforming to that protocol
#protocol SetUpNavDeleagte;
#interface SetUpNavViewController : UIViewController
#property (strong,nonatomic) id <SetUpNavdelegate> initiator;
// the rest
#end
#protocol SetUpNavdelegate <NSObject>
-(void)setupFinished;
#end
In your InitialVC' viewDidLoad you do:
Check Your defaults are set properly or not by determine "firstTime" BOOL value
and fire setUpNav controller in code
-(void)viewDidLoad
{
// Check your defaults for consistency
if (firstTime){
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"Main OR you SB name" bundle:nil];
UIViewController *setUpNavVC = [loginStoryboard instantiateViewControllerWithIdentifier:#"SetUpNav"];
setUpNavVC.initiator = self;
YouAppDelegateClass *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = setUpVC;
// Here you don't need animation as I assume it is the very first screen
}
// ... the rest
}
-(void)setupFinished
{
// Here You animately restoring your initial vc.
YouAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.window.rootViewController = self; // This line is for device orientation sync
[UIView transitionWithView:appDelegate.window
duration:FINISH_DURATION
options:UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionCurveEaseInOut
animations:^{ appDelegate.window.rootViewController = self; }
completion:nil];
// setUpNav controller will be dealloced by ARC
}
In your setUpNavViewController after you finished all defaults job you set notification for your delegation:
[self.initator setupFinished];
In may app this setting work is actually a separate storyboard with it's own workflow in it. By the way using this approach you are able not only show it first time but whenever your app's user defaults is not set properly (like if you using settings bundle). You can show it modally, in navigation stack or in pop over (iPad case). It is a more generic approach.
Summary: The one who starts, he finishes.

Turn off uiswitch from AppDelegate

I'm using LocalNotifications in my AppDelegate.m
If the user has the app open, the notification comes in the form of an alert.
The AppDelegate.m receives the clickedButtonAtIndex event. Regardless of the current view the user sees, the alert shows and everything works fine so far.
However, when receiving the event, I'd like to change the state of a UISwitch that exists on a UIVIewController.
EDIT: ADDED MORE CODE
My App is set up this way:
AppDelegate.m has this code:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
// Called from the local notification above when the View button is clicked and the app reopens
//called when app is open vs in background
NSLog(#"got notification");
UIApplicationState state=[application applicationState];
if(state==UIApplicationStateActive){
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:#"Notice"
message:notification.alertBody
delegate:self cancelButtonTitle:#"Sleep"
otherButtonTitles:#"Turn Off", nil];
[alert show];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"clicked button");
if(buttonIndex==1){
SettingsPage *setPage = [[SettingsPage alloc] initWithNibName:nil bundle:nil];
[setPage clickedAlert];
}
}
SettingsPage.m has this code:
#interface SettingsPage()
#property (weak, nonatomic) IBOutlet UISwitch *alarmSwitch;
#end
#implementation SettingsPage
-(IBAction)setAlarm{
//clear all notifications before setting a new one
[[UIApplication sharedApplication] cancelAllLocalNotifications];
//set a new LocalNotification
UILocalNotification *localNotification=localNotification =[[UILocalNotification alloc] init];
if(localNotification!=nil){
localNotification.fireDate=[NSDate dateWithTimeIntervalSinceNow:60]; //seconds
localNotification.timeZone=[NSTimeZone defaultTimeZone];
localNotification.alertBody=#"Reminder!";
localNotification.hasAction=YES; //fires didreceive event, opens app
localNotification.soundName=UILocalNotificationDefaultSoundName;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; }
}
-(void)clickedAlert{
NSLog(#"clicked alert");
[self.alarmSwitch setOn:NO animated:YES];
}
This has the desired effect of setting the "alarmSwitch" to "Off" (and thus canceling further notices), but the switch itself still shows in the view as "On" (green).
How can I flip the actual switch on the SettingsPage via code from my AppDelegate.m so that it behaves the same as if the user did it (i.e. changes it's visual and executes the connected method)?
As CrimsonChris mentioned, you seem to be making a new instance of SettingsPage every time, thus you're not seeing the change you want.
You could fire off an NSNotification,
[[NSNotificationCenter defaultCenter] postNotificationName:#"ClickedButtonAtIndex1" object:nil];
..and listen to it in your UIViewController.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleIndex1Clicked) name:#"ClickedButtonAtIndex1" object:nil];
with your UIViewController doing what it needs to in the selector method:
-(void)handleIndex1Clicked
{
[self.setPage.alarmSwitch setOn:NO animated:YES];
}
PS. I'd suggest having extern const NSStrings holding your observer names.
Hope that helps!
It looks like you're getting a new SettingsPage and then setting its alarmSwitch to "Off". What you probably want is to get the existing SettingsPage instead of making a new one with alloc init.

Resources