Automatically Loading At App Start - objective-c

[Cocoa/Objective-C]
I adapted a timer routine (for current time) from this site (and it works great - thanks). It's currently attached to a button.
My question is: How do I make it start when my app starts up (and not use a button) (in other languages, I'd simply put the action listener or timer in the Form)...?
Thank for any help on this!

In your application delegate you'll find a method called
- (void)applicationDidFinishLaunching:(UIApplication *)application
I guess that would be where to start a timer on app startup.

Put it in your awakeFromNib method. This is called on all objects that get deserialized from your nib (like your application delegate), but it isn't called until all objects are deserialized and wired (so you can use your text field, for instance). For example:
- (void)timerFired:(NSTimer*)timer
{
NSLog(#"Timer completed!");
}
- (void)awakeFromNib
{
[NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:#selector(timerFired:) userInfo:nil repeats:NO];
}
Obviously, in this simple example the timer could have been created in either the applicationDidFinishLaunching: method or the awakeFromNib method since it doesn't interact with any other serialized objects, but in your case, it sounds like you need the awakeFromNib method.

Related

Should I write NSTimer code in view controller or separate class with either delegation/notification pattern?

I have 5 buttons in my MotorViewController that act as on/off switches for 5 motors. Press button A, motor A will run indefinitely 'til you press the button again to stop it.
I've just added a 6th button that will tell the motor A to run for 2 minutes. I've added the NSTimer code in my ViewController and everything works fine. After 2 minutes, I call my method, runPump, and the motor shuts off automatically.
I've been optimizing my MotorViewController quite heavily, and this will be the first time optimizing for an NSTimer.
Here's the code:
#import "MotorViewController.h"
#interface MotorViewController()
#property (nonatomic, strong) NSTimer *counterTimer;
#end
#implementation MotorViewController
{
int _count;
}
- (void)viewDidLoad
{
_count = 0;
}
// called from the 6th button action method (code is implied)
- (void)setupTimerForCalib
{
self.counterTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerCount)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.counterTimer forMode:NSRunLoopCommonModes];
NSLog(#"timer started");
}
- (void)timerCount {
_count++;
NSLog(#"count: %d", _count);
if (_count == 120) {
_count = 0;
[self.counterTimer invalidate];
NSLog(#"timer ended");
// timer has ended, shut pump A (SALINE) off
[self setPumpInfo:SALINE select:0];
[self runPump];
}
}
I have another view controller that I'd like to have use these methods, so a better reason yet to not just keep them in MotorViewController.
Should I keep these NSTimer methods within MotorViewController, or create a delegation class for them? Or (after grazing around on the web a bit), set up an NSNotification that, after the 2 minutes, calls setPumpInfo:select: and runPump?
Whichever the best option, could you also explain the reasoning for that over the other. I'm trying to learn more about design patterns and know how to use them in the right scenarios. Thanks!
I would have an NSObject subclass modelling your pump.
I would give this a setInfo and both a run and stop method (at least).
Your ViewControllers should be controlling the views and interacting with your models, so they would create the new pump object (model) that they are interacting with.
Now, you might want to add another method to your Pump: runAfterDelay:(NSTimeInterval)delay forDuration:(NSTimeInterval) duration and embed the NSTimer within the Pump class.
You can then use pumps in your view controllers as follows:
-(void) startPump {
[self.pump setInfo:SALINE select:0];
[self.pump runAfterDelay: 120 forDuration: 120];
}
Keep the logic out of your view controllers, so you don't have to replicate it.

Any bugs or changes in NSTimer with OC?

I'm testing NSTimer with OC and Swift. When I write with OC, here is something I don't quite understand: is it REQUIRED to add the timer to the NSRunLoop after the timer is inited? If not required, why can't it be invoked in a loop even if I set the repeats to YES?
Here is my code, I just init a timer and set repeats to YES, and what I expect is the code timerTick: should be invoked every 2 seconds, but it doesn't work as I expect... until I add the timer to the NSRunLoop.
OC:
-(void)viewDidLoad {
[super viewDidLoad];
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
}
-(void) timerTick:(NSTimer*) timer{
NSLog(#"ticked,%#",#"aa");
}
I rewrote the same code with Swift
As you can see, I don't add the timer to NSRunLoop, however it works as I expected: the runTimeCode method is invoked every 2 seconds.
var timer:NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: "runTimeCode", userInfo: nil, repeats: true)
}
func runTimeCode(){
NSLog("ticked")
}
My Question
Is there any bug in OC with iOS9.2 of NSTimer? I searched a lot with Google, but I didn't find anything saying it is REQUIRED if you want to let the timer works correctly.
How do I use NSTimer?
Is NSTimer usage different between OC and Swift?
First of all, you are using different methods. How come you would get the same result? scheduledTimerWithTimeInterval will automatically add to the NSRunLoop after initializing it. However, timerWithTimeInterval won't. You will need to manually call fire or add it to the NSRunLoop in order to trigger it.
Just like the methods' name mean, scheduledTimerWithTimeInterval will do scheduling for you. As for timerWithTimeInterval, it just gives you a timer. Apple is pretty restricted on the names. Sometimes, you can guess its purpose based on it.
With Objective-C, you should try this method :
NSTimer *timer = [NSTimer scheduleTimerWithTimeInterval:2 target:self userinfo:...];
You can see all method's content in Xcode.

WebView did finish launching does not work

what is wrong with this method?
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
[activityIndicator stopAnimation:self];
}
I want to stop the Circular Progress Indicator (activityIndicator). But there is something wrong with - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame. I am coding for mac osx and not for iOS. I heard something from Delegates, what does that mean?
Check to be sure that your UIWebView has set its delegate. Setting a delegate is basically telling the program who you want to handle events (like taps, gestures, or, in this case, the loading of a webView). Thus when an event is fired, it will inform the delegate and the delegate can process it. Maybe if you post more of your code it would help, but I would check your declaration of the UIWebView in question. Be sure that after you allocate it and initialize it, you set its delegate to self (assuming that this method is in the same class), like so:
UIWebView *myWebView = [[UIWebView alloc] init];
[myWebView setDelegate:self];
If you have not set the delegate, it is firing off events and no one is receiving them to process them. The method you are using is waiting for the specific event sent by any webView. When it is sent an event message it passes, as a parameter, the webView that triggered. In any case, put in a log statement to be sure you are entering the method. That will tell you if it is receiving the event messages.
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSLog(#"Did finish loading...");
[activityIndicator stopAnimation:self];
}
NOTE: This is as per iOS experience, but should work for Mac OS as well. Let me know what your log result is, if the method is getting called or not.

Flipping a UISwitch with an NSTimer

I'm trying to learn how to use NSTimers, and I thought of the following: Create a switch. Let the timer begin as the app begins, and after each second, a function that changes the state of the switch is called.
Here's what I did so far:
I declared both the timer and the switch in the header file ViewControl.h:
//Timer
{NSTimer *timer;}
#property (weak, nonatomic) IBOutlet UISwitch *zeSwitch;
Then, in the ViewControl.m file I defined the following:
- (IBAction)zeSwitch:(id)sender {
UISwitch *zeSwitchSatus = (UISwitch *) sender;
BOOL yn = zeSwitchSatus.isOn;
[zeSwitch setOn:yn animated:YES];
}
- (void)viewDidLoad
{
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:#selector(zeSwitch) userInfo:nil repeats:YES];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
So my hope was that when I run the code, I'll see a switch that is on or off. Then I'll see it changing its status automatically with time, without me interfering.
But that didn't work! I first get the image above. Nothing changes. Then it crashes when I press the switch. (But my idea is not to touch it at all.)
Any ideas?
You're pretty close. There's a few things wrong here. First, the method that you're giving to the timer is named zeSwitch: -- the colon is significant. So you need to create the timer like this:
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(zeSwitch:)
userInfo:nil //^ Note colon!
repeats:YES];
The method named zeSwitch without the colon is actually the accessor method for the switch, because you've named your property zeSwitch. You should really rename the timer's action method to clarify this. Right now, the timer is calling the accessor method for the switch every second, which doesn't really do anything.
Next, the timer passes itself to the method it calls. The sender argument in zeSwitch: is going to be the timer, not the switch. If this method was actually being called via the timer, you would get a crash because you'd be sending isOn to the timer, and it doesn't respond to that.
You've got an outlet to the switch, so you can refer to it via that outlet:
- (void)flipSwitch: (NSTimer *)tim
{
BOOL switchIsOn = [[self zeSwitch] isOn];
Notice that I've corrected the names and types in this method -- you'll also need to change the timer creation to reflect this: #selector(flipSwitch:).
Third, you want to flip the switch, so you should be setting it to the opposite of its current status. The next line needs to be:
[[self zeSwitch] setOn:!switchIsOn animated:YES];
The ! operator negates the BOOL to which it's attached, turning YES into NO and vice versa.
1) When you specify a selector that takes one parameter, you need a colon after the name, so #selector(zeSwitch:).
2) The selector that is triggered by a timer gets the timer as a parameter, not a switch, so - (IBAction)zeSwitch:(NSTimer *)timer.

How to update NSMenu while it's open?

I have a NSMenu with dynamically added NSMenuItems. The NSMenu is not refreshing properly while it's kept open. I am calling NSMenu update method in NSEventTrackingRunLoopModes.
I have implemented following methods to update NSMenu.
- (void)menuNeedsUpdate:(NSMenu *)menu {
for (NSInteger index = 0; index < count; index++)
[self menu:menu updateItem:[menu itemAtIndex:index]
atIndex:index
shouldCancel:NO];
}
- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel`
- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
Updating the menu items in NSEventTrackingRunLoopMode solved this issue.
I am dynamically populating menu items in a timer and NSMenu is not updating while it's open.
Make sure to have the timer fire on the respective run mode:
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
You might only have it fire on NSDefaultRunLoopMode right now.
You have left out a bunch of the code. However, you aren't supposed to call -menu:updateItem:atIndex:shouldCancel:. That's a method that you're supposed to implement and the framework is supposed to call it.
Also, generally you're only suppose to implement either -menuNeedsUpdate: or both of -numberOfItemsInMenu: and -menu:updateItem:atIndex:shouldCancel:. Implementing all three doesn't make much sense. You implement the former if you can build the menu immediately. You implement the latter two if building the menu will take some time.
Finally, all of those methods are documented as being called "when a menu is about to be displayed". I'm not terribly surprised that they're not called repeatedly while the menu is open.
If you know the menu needs to be updated, you can try invoking -[NSMenu update]. That may provoke it to call your delegate methods. I'm pretty sure you can also just call the methods on NSMenu and NSMenuItem to modify the menu, without waiting for your delegate to be called.