I'm trying to make my timer "run" in the background by saving the time to disk on entering background and retrieving it on entering foreground. Each view controller has a timer and a timeInterval specified by the user. The problem is, I don't know how to access the timeInterval variable. I think I can get the difference of time by using something like this (would this work?):
NSTimeInterval idleTime = [dateReturnedToForeground timeIntervalSinceDate:dateEnteredBackground];
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate:startDate];
elapsedTime -= idleTime;
Each view controller (and timer/time interval) is accessed like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC;
if (![self.detailViewsDictionary.allKeys containsObject:indexPath]){
detailVC = [[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
[self.detailViewsDictionary setObject:detailVC forKey:indexPath];
detailVC.context = self.managedObjectContext;
}else{
detailVC = self.detailViewsDictionary[indexPath];
}
Tasks *task = [[self fetchedResultsController] objectAtIndexPath:indexPath];
detailVC.testTask = task;
[[self navigationController] pushViewController:detailVC animated:YES];
NSLog(#"%#", self.detailViewsDictionary);
[[NSNotificationCenter defaultCenter] addObserver:detailVC forKeyPath:self.detailViewsDictionary[indexPath] options:nil context:nil];
}
I am adding each view controller to the Notification Center so it can be accessed in the app delegate (i think this is right?). Problem is, I don't know how to combine the first code with the view controller code, because I can't access the view controller's properties in the app delegate. Any suggestions so that I can make my timer "run" in the background?
You are going about this all wrong. There is no need to do any of this in the app delegate.
Have each view controller listen for the UIApplicationWillResignActiveNotification notification. Then each view controller can do whatever it feels is appropriate to save its data when the notification is received.
Update:
In the view controller's init... method, register for the notification:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(resigningActive) name:UIApplicationWillResignActiveNotification object:nil];
In the view controller's dealloc method, unregister:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}
Then implement the resigningActive method:
- (void)resigningActive {
// The app is resigning active - do whatever this view controller needs to do
}
Related
I am very new to iOS development and I'm struggling to make an app which connects to BLE devices. As I have many view controllers I need to keep the peripheral always connected in all of them.
To achieve this, I implemented all the BLE connection methods in a Singleton. This works just great, I call the connect method from View Controller and the Singleton connects to the peripheral.
Now, the problem is I have a UILabel in my view controller which I would like to update with the connection state (scanning, connecting, connected, disconnected) from the Singleton.
So I tried to get instance from the View Controller and change the label directly like:
MainViewController *controller = [[MainViewController alloc] init];
controller.myLabel.text = #"TEST";
I also instantiated the view controller class like:
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MyStoryboard" bundle: nil];
MainViewController *controller = (MainViewController*)[mainStoryboard instantiateViewControllerWithIdentifier:#"MainVC"];
Then I tried to create a method in the main View Controller:
- (void) updateLabel:(NSString *) labelText{
NSLog(#"CALLED IN MAIN");
self.myLabel.text = labelText;
}
And call it from Singleton like:
MainViewController *controller = [[MainViewController alloc] init];
[controller updateLabel:#"TEST"]
Which was called properly (NSLog was shown) but the label was not updated.
I don't really know how to update my View Controller label from the Singleton. Don't know neither if the way I'm trying to do it is the right one or not.
Any advice or help would be much appreciated. Thanks.
----- UPDATE: -----
Thanks to Mundi and Nikita, I got a better way to implement what I need through NSNotification. For all those who need it here is how I do it:
In my View Controller in viewDidLoad I call:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateConnectionLabel:) name:#"connectionLabelNotification" object:nil];
Then in the same class I implement the notification observer method like:
- (void)updateConnectionLabel:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"connectionLabelNotification"]) {
self.connectionLabel.text = notification.object; //The object is a NSString
}
}
Then in my Singleton, when I need I call:
[[NSNotificationCenter defaultCenter] postNotificationName:#"connectionLabelNotification" object:[NSString stringWithFormat:#"CONNECTED"]];
When the View Controller receives the notification from the Singleton it updates the label with the text I add on the notification object (in this case #"CONNECTED").
You need to use NSNotification.
Here is sample code:
in viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mySelector:)
name:DeviceStateChanged
object:nil];
in dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self
name:DeviceStateChanged
object:nil];
also add a method in ViewController:
- (void) mySelector:(NSNotification *) notification {
// action performed
}
in Sigleton
- (void) foo {
/// some actions
// device connected
[[NSNotificationCenter defaultCenter] postNotificationName:DeviceStateChanged object:self];
///
}
Recommendation: move notification name to your constants and use constant name. For naming convention look at Apple guidelines
The proper way to do this is via NSNotification. This communication device is meant for exactly this kind of situation. It broadcast a message without caring whether the potential receiver is available.
In your view controllers, you call NSNotificationCenter's addObserver / removeObserver when they appear / disappear. You post the notification via postNotification:.
So i have an ios app which has Two Tabs. First tab has a UIGestureRecognizer which calls a countdown Method once its TAPPED.
Second tab has a UIPickerView with Three selections.
This is what i have in my secondTab's ViewController in code:
FirstViewController *firstvs = [[FirstViewController alloc] init];
NSInteger selectedRow = [self.pickerView selectedRowInComponent: 0];
if (selectedRow == 0) {
NSLog(#"Invalid Row"); // This block will not do nothing
}
else if (selectedRow == 1) {
// Call a method in firstViewController
[firstvs smallSelection];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"smallSelection2"
object:self];
}
else if (selectedRow == 2) {
[firstvs mediumSelection];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"mediumSelection3"
object:self];
}
else if (selectedRow == 3){
[firstvs largeSelection];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"largeSelection"
object:self];
}
What this basically does is if the USER selects lets say, Row 2, Within the same secondTab it dispays in a UILabel what the user has selected.
Now then, that selection is broadcasted in a NSNotificationCenter as shown in the code under each if and else block.
*I have 3 NSnotificationCenter for each if Statement which i clearly dont know if its safe to do this or not aside from my problem.
So when the user selects Option 2 which would be row 2, In the First tab's ViewController, it calls a method called mediumSelection.
In FirstViewController.m :
-(void)mediumSelection {
// Other functions
}
But as you may notice i rather use an NSNotificationCenter to keep the firstViewController Listening to the secondViewController's instead of just executing this method as i been recommended to use the NSNotificationCenter.
Notice this NSNotification Broadcaster in the selection 2 on secondViewController:
[[NSNotificationCenter defaultCenter]
postNotificationName:#"mediumSelection3"object:self];
This Broadcaster then sends a message to the listener as i show the code from the firstViewController Now:
- (id) init
{
self = [super init];
if (!self) return nil;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(smallSelection2:)
name:#"smallSelection2"
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mediumSelection3:)
name:#"mediumSelection3"
object:nil];
return self;
}
- (void) smallSelection2:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"smallSelection2"])
NSLog (#"SmallSelection is successfully received!");
}
- (void) mediumSelection3:(NSNotification *) notification
{
if ([[notification name] isEqualToString:#"mediumSelection3"])
NSLog (#"mediumSelection is successfully received!");
}
So this works, It logs it into the Console as the the UIPicker is being used to scroll it recieves the selection. Now for the BIG problem and the main reason this NSNotification is meant for.
Before this Reciever code, Above i have the UIGestureRecognizer and the countdown method called from the Tap as Follows:
-(void) viewDidLoad {
UITapGestureRecognizer *tapScreen = [[UITapGestureRecognizer alloc] initWithTarget: self action:#selector(tapScreen:)];
[self.view addGestureRecognizer: tapScreen];
}
- (void) tapScreen:(UIGestureRecognizer *)gr
{
NSLog(#"Screen has been tapped");
/*
CODE RELATED TO LABELS, NOTHING IMPORTANT
*/
timer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: #selector(countdown) userInfo: nil repeats: YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];
}
-(void) countdown {
/*
MORE CODE BEING EXECUTED ON THE VIEW
*/
NSLog(#"Program success: counting... %i", mainInteger);
self.hoursLeft.text = [NSString stringWithFormat:#"%i hours", mainInteger];
self.percentGauge.text = [NSString stringWithFormat:#"%1.2f", subInteger];
// FIGURE 1:
[self smallSelection];
}
Now the Problem is i want the countdown to begin according to the selection Made from the NSNotificationCenter. So if the user selected Row 2 from the Picker, the countdown goes from that selection according to the methods "Small, Medium, Large".
as you can see, in the end of the countdown method where it says "FIGURE 1:" I am manually calling the smallSelection Method which i would like to embed an If statement to check the 3 possible choices according to the UIPicker selection, i am using NSNotificationCenter because i was testing if this was would work aside from the instance i set on secondViewController
called: [firstvs smallSelection].
So is there anyway to add an if statement to check the 3 possible choices and to execute that method on a tap ?
I would use a global object where all values are written down and change these values from different the viewcontrollers.
So you can get the values from all viewcontrollers using this global object:
I have a view I want to display on a certain event. My view controller is listening for a broadcast notification sent by the model and it attempts to display the view when it receives the broadcast.
However the view is not appearing. BUT if I run the exact same view code from elsewhere within the View Controller then it will be displayed. Here's some code from the VC to illustrate:
- (void) displayRequestDialog
{
MyView *view = (MyView*)[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] objectAtIndex:0];
view.backgroundColor = [UIColor lightGrayColor];
view.center = self.view.window.center;
view.alpha = 1.0;
[self.view addSubview:view];
}
- (void) requestReceived: (NSNotification*) notification
{
[self displayRequestDialog];
}
When the above code is run the view does not appear. However if I add the call to displayRequestDialog elsewhere, for example to viewDidAppear:
- (void) viewDidAppear
{
[self displayRequestDialog];
}
Then it is displayed.
My question therefore obviously is why can I get the view to successfully appear if I call displayRequestDialog from viewDidLoad, but it will not display if called from within requestReceived?
(Note that I am not calling requestReceived prematurely before the view controller / its view has loaded and displayed)
At first I was posting the notification like this:
[[NSNotificationCenter defaultCenter] postNotificationName: kMyRequestReceived
object: self
userInfo: dictionary];
Then I tried this:
NSNotification *notification = [NSNotification notificationWithName:kMyRequestReceived object:self userInfo:dictionary];
NSNotificationQueue *queue = [NSNotificationQueue defaultQueue];
[queue enqueueNotification:notification postingStyle:NSPostWhenIdle];
Then I tried this:
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName: kMyRequestReceived
object: self
userInfo: dictionary];
});
Then I tried this:
[self performSelectorOnMainThread:#selector(postNotificationOnMainThread:) withObject:dictionary waitUntilDone:NO];
- (void) postNotificationOnMainThread: (NSDictionary*) dict
{
[[NSNotificationCenter defaultCenter] postNotificationName: kMyRequestReceived
object: self
userInfo: dict];
}
And I have tried invoking displayRequestDialog like this:
dispatch_async(dispatch_get_main_queue(),^{
[self displayRequestDialog];
});
I have found the cause of the view not displaying - the frame's origin is getting negative values when invoked via the notification code but positive values when invoked otherwise and thus was being displayed off the screen.
No idea why there should be a difference however.
You are not listening for the notification. Do so like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(displayRequestDialog) name:kMyRequestReceived object:nil];
As far as we cannot see the code you use to register your controller to receive notifications I would recommend you to use the observer registration method which enforce getting notifications on the main thread "for free"
[[NSNotificationCenter defaultCenter] addObserverForName:#"Notification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSLog(#"Handle notification on the main thread");
}];
I have in my app a UITableview Controller, a View Controller and I'm trying to pass NSDictionary from UITableview Controller to my ViewController, using NSNotificationCenter. So, I push a notification at my UITableview Controller and then I add an observer ,using a selector at my ViewController.The selector is called,but I have an NSLog and get memory results ,like :
ViewController: 0x8a0bcc0
I have tried to pass NSString instead of NSDictionary , but I get again memory results , and not the value of the string.
My code :
UITableView Controller
NSString *string=#"This is a test string";
[[NSNotificationCenter defaultCenter] postNotificationName: #"Update" object: string];
ViewController
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"Update" object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self];
And here is the incomingNotification selector method:
-(void) incomingNotification : (NSNotification *)notification{
NSLog(#"Trying to print : %#",[notification object]);
}
All Notifications take place at ViewDidLoad method.Thank you!
UPDATE
Finally , I quit using NSNotificationCenter and used properties to pass data ,changing a bit the inheretence from my TableViewController. No idea why Notifications did not work ,as they were supposed to. Thank you all ,very much for your suggestions and ideas :)
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self]
Object means the object that generates a notification. To post parameters use another method
[[NSNotificationCenter defaultCenter] postNotificationName:#"Update" object:self userInfo:string]
If I understand correctly, UIViewController is shown after you tap a button on UITableViewController. And you if you are adding a ViewController as observer in its -viewDidLoad:, then it will be able to receive notifications only when it is loaded.
What do you need:
1) override -init or -initWithNibName: method of ViewController like this:
-(id) init
{
self = [super init];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(incomingNotification:) name:#"Update" object:nil];
}
return self;
}
so you can be sure ViewController is observing for notifications from the beginning (well, this might be unnecessary step for your case)
2) when you push ViewController you need to send a notification after it was created, like this:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ViewController *nextController = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:nextController animated:YES];
NSString *string=#"This is a test string";
[[NSNotificationCenter defaultCenter] postNotificationName: #"Update" object: string];
}
However, if you're trying just to send some parameters from one view controller to another, this is the wrong way. Just create a property in ViewController and in method -tableView:didSelectRowAtIndex: of UITableViewController set this property
I have an exception like this:
Thread 1: EXC_Breakpoint (code = EXC_I386_BPT,subcode=0x0)
In my story board i have 3 controllers. Navigation controller, UIViewController and then a tableviewcontroller. When the app first launches UIViewController is shown. I add some stuff to the Core Data DB. Then on this controller itself i have a "Check Records" bar button. I click that and move to the 3rd controller the tableviewcontroller. Here i can see the records that i added on that day. I click the one i just added and traverse back to the UIviewcontroller screen using NSNotifications like this:
//This code is in tableviewcontroller.didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.salesOrderObject = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
[[NSNotificationCenter defaultCenter] postNotification : notification];
[self.navigationController popViewControllerAnimated:YES];
}
In my ViewController viewDidLoad i write this code to listen to the notification.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(newReloadData)
name:#"reloadRequest"
object:nil];
-(void)newReloadData
{
self.salesOrder = self.appDelegate.salesOrderObject; //self.reloadedSOObject; //appDelegate.salesOrderObject;
if(self.salesOrder !=nil)
{
//I add UItextviews,textfields,buttons etc here to a scrollview.
ITMCustomView *cView = [[ITMCustomView alloc] initWithFrame:CGRectMake(187, 660, 400, 400)
andOrderedItem:#"New"
andTag:self.tagNumber
withFoldArray:self.foldTypeArray
withRollArray:self.rollTypeArray];
cView.tag =self.tagNumber;
NSLog(#"Assigned tag is %d",cView.tag);
cView.delegate = self;
//[self.scrollView addSubview:customView];
//self.scrollView.contentSize = CGRectMake(187, 660, 400, 400).size;
CGPoint scrollPoint = CGPointMake(0.0, (cView.frame.origin.y/500)*400);
[self.scrollView setContentOffset:scrollPoint animated:YES];
[self.scrollView addSubview:cView];
CGFloat scrollViewHeight = 0.0f;
for (UIView *view in self.scrollView.subviews)
{
scrollViewHeight += view.frame.size.height;
}
CGSize newSize = CGSizeMake(320,scrollViewHeight);
//CGRect newFrame = (CGRect){CGPointZero,newSize};
[self.scrollView setContentSize:newSize];
NSLog(#"750 the tag number is %d",self.tagNumber);
NSLog(#"the scroll view height is %f",scrollViewHeight);
self.currentCGRectLocation = cView.frame;
}
}
I am thinking if the exception occurs at adding something to scroll view.Or on looking some more on stack overflow it could be either because of delegate or NSnotification. I cant figure out why and where the exception occurs.Does writing this line of code give me this exception?
[self.navigationController popViewControllerAnimated:YES];
this question is extension of this question
Adding Customview to UIScrollview results in stuck screen when Scrolling
I have no idea where/how its getting this exception. If you need more info please ask. Thanks...
One thing might be when you call newReloadData you notify notification center that what class has#"reloadRequest" string with newReloadData method should run it, so you need to first pop the view then call the notification
Just try change the line order first call [self.navigationController popViewControllerAnimated:YES]; then call [[NSNotificationCenter defaultCenter] postNotification : notification];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ITMAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.salesOrderObject = [self.salesOrdersArray objectAtIndex:indexPath.row];
NSNotification *notification =[NSNotification notificationWithName:#"reloadRequest"
object:self];
//here first pop view
[self.navigationController popViewControllerAnimated:YES];
//then send notification before scopes end
[[NSNotificationCenter defaultCenter] postNotification : notification];
}