Creating control pad - objective-c

I am trying to code a controller for an image view to move using buttons.
inside up button code :
-(IBAction)upButton:(id)sender{
mainChac.center = CGPointMake(mainChac.center.x, mainChac.center.y - 5);
}
code is working properly but I have to tap it repeatedly to keep moving up, I wanna know which method to call, to move it, while holding the up button.
edit :
-(void)touchDownRepeat{
mainChac.center = CGPointMake(mainChac.center.x, mainChac.center.y - 5);
}
-(IBAction)upButton:(id)sender{
[upButton addTarget:self action:#selector(touchDownRepeat) forControlEvents:UIControlEventTouchDownRepeat];
}
edit 2: solution
-(void)moveUp{
mainChac.center = CGPointMake(mainChac.center.x, mainChac.center.y - 5);
}
- (void)holdDown
{
NSLog(#"hold Down");
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(moveUp) userInfo:nil repeats:YES];
}
- (void)holdRelease
{
NSLog(#"hold release");
[timer invalidate];
}
-(IBAction)upButton:(id)sender{
[upButton addTarget:self action:#selector(holdDown) forControlEvents:UIControlEventTouchDown];
[upButton addTarget:self action:#selector(holdRelease) forControlEvents:UIControlEventTouchUpInside];
}

EDIT:
What you need to to have 2 events, one for while the button is pressed down and another for when the button is released like so:
[aButton addTarget:self action:#selector(holdDown) forControlEvents:UIControlEventTouchDown];
[aButton addTarget:self action:#selector(holdRelease) forControlEvents:UIControlEventTouchUpInside];
- (void)holdDown
{
NSLog(#"hold Down");
}
- (void)holdRelease
{
NSLog(#"hold release");
}
Have your hold down function start a loop, and your holdRelease stop the loop.
EDIT-2:
An easy way (but perhaps not the best way) to achieve this loop would be to use NSTImer scheduledTimerWithTimeInterval inside the hold down and invalidate it in the release method.
all of this stuff has been done before, please try googling this stuff. Here is a stackoverlfow question with an example: ios scheduledTimerWithTimeInterval for amount of time

Related

Objective-C, Displaying an NSTimer activated by a UIButton

I'm working on a project for school that includes a set of buttons that create NStimers used as a countdown timer. Ideally, the time would display as the title of the UIButton that started the timer.
So far I have only been able make this work if when setting the display of the time I refer to a specific instance of a UIButton rather than a somehow passing a UIButton argument.
I also have declared "time" NSString that holds the current displayed value calculated by the NSTimer. This is not how the final implementation should be, and I only have it set up like this just to have it partially working.
Code for one of the buttons:
b1 = [[UIButton alloc]
initWithFrame:CGRectMake(neutral.frame.size.width * 0.56, neutral.frame.size.height * 0.18, neutral.frame.size.height * 0.64, neutral.frame.size.height * 0.64)];
[b1.titleLabel setFont:[UIFont fontWithName:#"Helvetica-Bold" size:35.0]];
[b1 setBackgroundImage: forState:UIControlStateNormal];
Method called from button press action:
-(void)buttonClicked:(id)sender
{
//do stuff
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(timerFired) userInfo:[NSNumber numberWithInt:t] repeats:YES]; }
NSTimer timer fired method:
-(void)timerFired
{
if((curMin>0 || curSec>=0) && curMin>=0)
{
if(curSec==0)
{
curMin-=1;
curSec=59;
}
else if(curSec>0)
{
curSec-=1;
}
if(curMin>-1)
{
time = [NSString stringWithFormat: #"%d%#%02d",curMin,#":",curSec];
[b1 setTitle:time forState:UIControlStateNormal];
}
}
else
{
[b1 setTitle: nil forState:UIControlStateNormal];
[timer invalidate];
}
}
I hope that was clear enough as to what I am trying to accomplish. I am somewhat surprised that I haven't been able to find anything to walk me through this. I am flexible to changing how exactly this works. All that really matters is that each button can start a unique countdown timer.
I'm sure that this code is sloppy, but Objective C is relatively new to me so any help at all would be appreciated.
Don't store time as minutes/seconds, just use seconds, as all that greater than 60 logic is really tedious isn't it.
I've chosen to call the instance variable _secondsLeft so I will know what it means when I look at the code in 6 months time:
-(void)timerFired
{
NSString *formatted = #"";
if (secondsLeft > 0)
{
secondsLeft--;
formatted = [NSString stringWithFormat:#"%d:%02d", (secondsLeft / 60), (secondsLeft % 60)];
} else {
[timer invalidate];
}
[b1 setTitle:formatted forState:UIControlStateNormal];
}

Press and hold to make Image move

I have a button and an image. When the button is pressed and held I want the image to movie across the screen quickly. Right now after I press and hold it just moves after I let go. I need it to start moving after a certain amount of time and stop when I let go.
here is what I have:
- (IBAction)buttonDown:(id)sender
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
x = x + 100;
}
With below code, you have to push 3 seconds to move _imageView, and then the image begins moving, and when you release the button, the image stops.
- (IBAction)pushToMove:(id)sender {
[self performSelector:#selector(move) withObject:nil afterDelay:3.0];
}
- (void)move
{
_nt = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(goRight)
userInfo:nil
repeats:YES];
}
- (void)goRight
{
_imageView.frame = CGRectMake(_imageView.frame.origin.x + 10, _imageView.frame.origin.y,
_imageView.frame.size.width, _imageView.frame.size.height);
}
- (IBAction)stop
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(move) object:nil];
[_nt invalidate];
}
I put this sample project to GitHub. You can download and just run it.
https://github.com/weed/p120804_ImageMoveDuringPushButton
This can be done in interface builder by simply liking to the buttons touch down property instead of touch up inside. In code it looks like this:
[myButton addTarget:self action:#selector(myMethod) forControlEvents:UIControlEventTouchDown];
In addition to this, you'll want to do some state checking on your animation, and use UIViewAnimationOptionBeginFromCurrentState as an option to help it pick up where it left off when the user presses the button again.

How to pass multiple values into #selector( ) for a UIButton?

In other words: I have one button with two events, how to capture UIControlEventTouchDown and UIControlEventTouchUpInside in the callback function setRedPos?
[btnRedPos addTarget:self action:#selector(setRedPos:) forControlEvents:UIControlEventTouchDown];// I want to pass in a value of 0
[btnRedPos addTarget:self action:#selector(setRedPos:) forControlEvents:UIControlEventTouchUpInside];// I want to pass in a value of 1
...
- (void) setRedPos:(UIButton*)btn
{
}
You can't pass arbitrary parameters via target/action. The first parameter is sender, and the second (if you set it up this way) is the event. You could use the event to tell what kind of event triggered it, like so:
[btnRedPos addTarget:self action:#selector(setRedPos:forEvent:)
forControlEvents:UIControlEventTouchDown];
[btnRedPos addTarget:self action:#selector(setRedPos:forEvent:)
forControlEvents:UIControlEventTouchUpInside];
- (void) setRedPos:(id)sender forEvent:(UIEvent*)event
{
UITouch* aTouch = [[event allTouches] anyObject];
if( aTouch.phase == UITouchPhaseBegan ) {
NSLog( #"touch began" );
}
else if( aTouch.phase == UITouchPhaseEnded ) {
NSLog( #"touch ended" );
}
}
Use two separate selectors.
- (void)setRedPosDown:(UIButton *)button {
[self setRedPos:button state:0];
}
- (void)setRedPosUp:(UIButton *)button {
[self setRedPos:button state:1];
}
[btnRedPos addTarget:self action:#selector(setRedPosDown:) forControlEvents:UIControlEventTouchDown];
[btnRedPos addTarget:self action:#selector(setRedPosUp:) forControlEvents:UIControlEventTouchUpInside];
The only two ways I know to achieve that in one method are:
1) the method zpasternack describes
2) Using two separate buttons one for the touch up one for the touch down and test the sender.
The first method is better in your case since it is with only one button object.
The second would be useful if you were looking for one method achieving the actions of different buttons.
Try to stick the closer to the physical representation. One button but two different actions? The the code has only one button and tests the actions.

How to flash a button on UI thread?

I have a button on my UI that I would like to flash (turn on and then off again) every 800ms, once the button has been pressed. I do that with the following code:
- (void)flickEmergencyButton {
// Check whether an emergency is in progress...
if (model.emergencyInProgress) {
// ...and if so, flick the state
self.emergencyButton.selected = !self.emergencyButton.selected;
// Make this method be called again in 800ms
[self performSelector:#selector(flickEmergencyButton) withObject:nil afterDelay:0.8];
} else {
// ...otherwise, turn the button off
self.emergencyButton.selected = NO;
}
}
...which works really well, except: There is a UIScrollView on the UI as well and while the user has his finger down on it and is scrolling around, the button freezes. While I completely understand why that is, I am not sure what to do about it.
The performSelector:withObject:afterDelay message schedules the message to be send on the current thread, which is the main thread, ie. the UI tread and hence does not get to process the message until all other UI activity has come to an end. Correct? But I need to do this on the UI thread as I cannot select/un-select the button on any other thread, right? So what is the solution here?
I would recommend using Core Animation. Try something like this:
-(void) flash{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3f];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
if( emergency ){
// Start flashing
[UIView setAnimationRepeatCount:1000];
[UIView setAnimationRepeatAutoreverses:YES];
[btn setAlpha:0.0f];
}else{
// Stop flashing
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationRepeatCount:1];
[btn setAlpha:1.0f];
}
emergency = !emergency;
[UIView commitAnimations];
}
Where btn is declared as
#property(nonatomic, retain) IBOutlet UIButton *btn;
and emergency is a simple BOOL variable.
Call flash to start and to stop the animation.
In this example we animate the alpha attribute for simplicity, but you can do the same with the button backcolor, as Sam said in his answer, or whatever attribute you like.
Hope it helps.
UPDATE:
Regarding making the transition between two images, try calling imageFlash instead of flash:
-(void) imageFlash{
CABasicAnimation *imageAnimation = [CABasicAnimation animationWithKeyPath:#"contents"];
[btn setImage:normalState forState:UIControlStateNormal];
if( emergency ){
imageAnimation.duration = 0.5f;
imageAnimation.repeatCount = 1000;
}else{
imageAnimation.repeatCount = 1;
}
imageAnimation.fromValue = (id)normalState.CGImage;
imageAnimation.toValue = (id)emergencyState.CGImage;
[btn.imageView.layer addAnimation:imageAnimation forKey:#"animateContents"];
[btn setImage:normalState forState:UIControlStateNormal]; // Depending on what image you want after the animation.
emergency = !emergency;
}
Where normalState and emergencyState are the images you want to use:
Declared as:
UIImage *normalState;
UIImage *emergencyState;
Assigning the images:
normalState = [UIImage imageNamed:#"normal.png"];
emergencyState = [UIImage imageNamed:#"alert.png"];
Good luck!
While this feels like a job for CoreAnimation (perhaps animating the backgroundColor of a custom UIControl) you could accomplish this with an NSTimer running in the appropriate run loop mode.
e.x.
NSTimer *timer = [NSTimer timerWithTimeInterval:0.8f target:self selector:#selector(flicker:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
and when you want to stop animation:
[timer invalidate], timer = nil;
button.selected = NO;
By adding the timer to all NSRunLoopCommonModes, you not only add it to the default run loop mode but the mode it is in while user interaction is being continuously processed (UITrackingRunLoopMode.)
Apple's docs give a more thorough explanation of run loop modes and run loops in general.

load/refresh button in my custom web browser

So I'm building a custom web browser with a a lot of custom features, but I'm having a problem with something very simple. I have a NSTimer:
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(checkLoad) userInfo:nil repeats:YES];
and here is the method it calls:
-(void)checkLoad{
if (webView.loading) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
//add button to address bar
UIButton *stopButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
stopButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
[stopButton addTarget:self action:#selector(stop) forControlEvents:UIControlEventTouchUpInside];
addressBar.rightView = stopButton;
addressBar.rightViewMode = UITextFieldViewModeUnlessEditing;
}
else if (!(webView.loading)) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
//add button to address bar
UIButton *refreshButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
refreshButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
[refreshButton addTarget:self action:#selector(refresh) forControlEvents:UIControlEventTouchUpInside];
addressBar.rightView = refreshButton;
addressBar.rightViewMode = UITextFieldViewModeUnlessEditing;
}
}
the buttons show up just fine, and they change when the page is loading or not loading. but the interaction with them is very spastic. the refresh button works most of the time, but the stop button almost never works. I've played with the timing on the NSTimer making it longer and shorter to no avail.
also, I know the button images are wrong, but those are just placeholder's until I can get the refresh and stop loading images like the ones in safari.
any ideas? I am totally willing to do something completely different to detect when the page is loading (that would actually be preferred as I don't want an NSTimer looping every .2 seconds if I can avoid it but this was the only way I could find.
What's wrong with using the delegate methods of UIWebView? See UIWebViewDelegate.
Setting up timers that fire every small fraction of a second so you can check something is a 'code smell'.