I am using xcode and am having a problem moving a button automatically. I have this function that whenever I call it, I expect the button to move to the coordinates that I set:
[movebutton setCenter:CGPointMake(164,50)];
Previously I tried to set an NStimer in an IBAction function and then use the timer to call this movebutton function - the button moved but if I call the same function without an NStimer it no longer works.
The code for the timer is:
[NSTimer scheduledTimerWithTimeInterval:(0.1/3) target:self selector:#selector(movebutton:) userInfo:nil repeats:YES];
Can anyone spot what I am missing?
Update:
I try to print out the x and y coordinates of the button and actually the button position has been updated.
NSLog(#"x position %f",movebutton.frame.origin.x);
however, on the UI screen it does not reflect at all.
Try placing
[self.view layoutIfNeeded];
in your moveButton: method so that when it's called from the timer it knows to update the UI.
Where do you call -[UIButton setCenter] manually? Couldn't comment because I don't have permission to comment.
The NSTimer works because it fires the method every 0.1/3 because the NSTimer instance is added to run loop
Related
I've got a NSTimer which fires every few milliseconds to load and then assign a new image into a NSImageView. The NSTimer has been setup using various run loops (NSRunLoopCommonModes, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode) just so that it continues to do what it's doing when events are firing.
This works great however this ceases to work when the actual window is being dragged around the screen. I have confirmed that the timer is in fact firing during the drag, and the image is being assigned to the NSImageView as well. The only problem is that the screen contents don't refresh for some reason when the window is being dragged. This is possibly a built-in optimization but I'd like to disable this so that the imageview continues to refresh and re-draw even when the window is being dragged around.
I've tried the following inside the NSTimer firing block but this still did not force the contents of the NSImageView to be refreshed and redrawn:
- (void)animateTimerFired:(NSTimer *)aTimer {
// .... load imageObj
// set image
[imgBackgroundView setImage: imageObj];
// try and refresh the window forcibly
[[imgBackgroundView window] viewsNeedDisplay];
[[imgBackgroundView window] display];
[[imgBackgroundView window] update];
[[imgBackgroundView window] flushWindow];
}
Is there any way to disable this 'optimization'?
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.
I'm making a game that involves buttons moving across the screen. When one button reaches the edge of the screen without being tapped, you lose a bar of health.
-(void) moveStickFig:(NSTimer *)timer {
UIButton *stick = (UIButton *)timer.userInfo;
CGPoint oldPosition = stick.center;
stick.center = CGPointMake(oldPosition.x + 1 , oldPosition.y);
if (oldPosition.x == 900) {
[stick removeFromSuperview];
healthCount--;
NSLog(#"%d", healthCount);
}
}
When you click on a button it disapears using [btn removeFromSuperview] The problem with this is the button still exists and continues moving across the screen. Is there a way to delete it completely? I've tried [stick release] but for some reason it just causes the app to freeze
It looks like you're using a repeating timer to move the button. If you don't explicitly end that timer, the timer is going to keep running, and moving the button.
Normally when you send the removeFromSuperview message to something like a button, it would deallocate or "delete" that object. This is because when the button is added to the superview, the superview retains the button, giving it a retain count of 1, and when it's removed from the superview, it releases it, giving it a retain count of 0.
However, because the button is stored as the userInfo of the timer, the timer also retains the object giving it a retain count of 2, and after you remove it from the superview it still has a retain count of 1. If you simply send the release message to the button, it will lower the retain count to 0 and it will deallocate the button, but it won't stop the timer. The next time the timer runs, it will cause problems because you're trying to access deallocated memory.
What you really want to do is to invalidate the timer: [timer invalidate]. This will stop the timer, and the timer will send a release message to the button, causing the button to be deallocated.
NSTimer retains its userInfo, which is the button object in your case. You should kill the timer using [timer invalidate].
I'd like to reproduce this behavior in my iPad application.
I have a subView that contains four custom buttons.
The view has an alpha value of 0.0
I have another custom button outside of the view descripted above that is always visible.
When the user touches the visible button the view appear animating its alpha to 1.0 showing the other 4 buttons.
Now I'like to start a timer that fires the view fadeOut after 2 seconds
When and if the user interact (say touchDown or whatever) with the buttons the timers need to be reset.
In other words the view may disappear only when nobody touches a button inside It.
Can you help me with this.
I've managed to learn the basics of UIView animation but I don't know how to queue them.
My iPad has iOS 3.2.2. installed.
Sorry for the bad explanation but this is my first iPad application and my first obj-c project.
You'd keep an NSTimer instance variable for that. As soon as your view is completely visible, which you can notice by e.g. implementing the fade in animation's delegate, you initialize it like this:
_fadeTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(fade:) userInfo:nil repeats:NO];
Make sure _fadeTimer is an instance variable, you need to be able to reset it. Then implement the fade out method:
- (void)fade:(NSTimer *)aTimer {
// Forget about timer!
_fadeTimer = nil;
[UIView beginAnimations:nil context:NULL];
// fade here
[UIView commitAnimations];
}
Upon every user interaction you just call a method that delays the fade. To do this, delete and re-create the timer. Or change it's fire date:
- (void)delayFade {
[_fadeTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow: 2.0]];
}
PS: There is no need to explicitly retain the timer. It's retained by the runloop until it fires. After the callback, it will be released anyways. Just make sure you always reset the variable to nil, otherwise your app may crash on an invalid access. If you need to delete the time beofre it fired, call the invalidate method.
I'm working on a simple timer app, and I've created a NSStatusItem with a menu and I have some NSTextField labels that updates the timer labels (http://cld.ly/e81dqm) but when I click on the status item the NSTimer stops (and stops updating the labels)..... how can I get around this problem?
EDIT: here's the code that starts the timer:
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerDidUpdate:) userInfo:nil repeats:YES];
You should add timer into MainRunLoop as given below:
NSRunLoop * rl = [NSRunLoop mainRunLoop];
[rl addTimer:timer forMode:NSRunLoopCommonModes];
I'm guessing the timer resumes as soon as you stop interacting with the NSStatusItem? (After the menu's dismissed & mouse button released).
The user interaction puts the main run loop into a mode where it doesn't update timers, so if your label has to continually update, you'll probably need to move the NSTimer and the label drawing to a separate process or another thread.