I have a slider that when it begins to change the values, another label shows up (lets the user see what they are doing). The only problem is that I need to know when the user has finished editing the slider so I can make that UILabel go away again. Is there a way to do this? The code below shows what I do when the sliders value begins to change. Thank you for you help!
- (IBAction)sliderValueChanged:(UISlider *)sender {
tipPercentLabel.text = [NSString stringWithFormat:#"%.f", ([sender value] * 100)];
tipPercentLabel2.text = [NSString stringWithFormat:#"%.f", ([sender value] * 100)];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[tipPercentLabel setAlpha:0.3];
[tipPercentLabel2 setAlpha:1.0];
[UIView commitAnimations];
[self performSelector:#selector(autoTipCalc) withObject:nil afterDelay:0.01];
}
You have already added one target/action pair for UIControlEventValueChanged, all you need to do is call addTarget:action:forControlEvents: again with a different selector and a control event of UIControlEventTouchUpInside (or any other that you're interested in).
You can add target to your slider as if you would to UIButon handling UIControlEventTouchUpInside (do it also for outside). Optionally or alternatively you could perform a selector after delay each time the value changes to check when the slider value has last changed and remove the label if it has taken long enough.
Related
I am having difficulty getting a UITapGestureRecognizer to detect every tap that is occurring. For context, I am making a drumming app. I have gotten the tap gesture recognizer to work correctly, but the user should be able to tap very quickly to drum, and the recognizer is only picking up on let's say a tap every so often (if you try to tap as quickly as you can).
I am guessing this has to do with iOS categorizing very quick taps in a row as one gesture, but is there any way to get every time a finger goes down as a tap event?
I have tried using UILongPressGestureRecognizer with a small press duration.
I have tried setting the requiredNumberofTouches and requiredNumberofTaps to 1.
The code does not seem necessary to post in order to answer the question, but I will post it if you request it.
Any help would be most appreciated!
EDIT: I am adding my code, since it may help:
-(void)tap:(UITapGestureRecognizer *)tapGestureRecognizer {
CGPoint location = [tapGestureRecognizer locationInView:[self view]];
switch ([self validateTouchLocation:location]) {
case 0:
return;
case 1:
[[ASSoundManager sharedManager] playSoundNamed:#"SD0025" ofType:#"mp3"];
break;
case 2:
default:
[[ASSoundManager sharedManager] playSoundNamed:#"SD0010" ofType:#"mp3"];
break;
}
float tapWidth = 30;
ASDrumTapView *drumTapView = [[ASDrumTapView alloc] initWithFrame:CGRectMake(location.x - (tapWidth / 2), location.y - (tapWidth / 2), tapWidth, tapWidth)];
[drumTapView setBackgroundColor:[UIColor clearColor]];
[drumTapView setNeedsDisplay];
[[self view] addSubview:drumTapView];
float animDuration = 0.75;
CGRect frame = [drumTapView frame];
[UIView animateKeyframesWithDuration:animDuration
delay:0.0
options:0
animations:^{
[UIView addKeyframeWithRelativeStartTime:0
relativeDuration:animDuration
animations:^{
[drumTapView setFrame:CGRectInset(frame, -frame.size.width, -frame.size.height)];
}];
[UIView addKeyframeWithRelativeStartTime:0
relativeDuration:3*animDuration/5
animations:^{
[[drumTapView layer] setOpacity:0.0];
}];
}
completion:^(BOOL finished) {
[drumTapView removeFromSuperview];
}];
}
There could be many reason:
One possible reason could be fact that your audio engine cannot play next sound before previous sound has finished.
Check multipleTouchEnabled property on UIView
If your view is embedded in UIScrollView or any derived class, check delaysContentTouches property
While I appreciate everyone's input, I figured it out on my own.
In my code, I am running a keyframe animation with options set to 0. What this means is that during the animation, user interaction with the view is not enabled.
By setting options to UIKeyframeAnimationOptionAllowUserInteraction, I am able to pick up on taps even though one of the subviews is still in animation.
Hope this helps someone else!
We have seven buttons, on clicking of anyone among them, i need to perform animations on remaining buttons "in sequence".
At present am using the following code in for loop,but animation is done on all the buttons at once.
[UIView beginAnimations: #"Fade In" context:nil];
button.alpha = 0;
[UIView setAnimationDelay:0];
[button setTitle:#"helloworld" forState:UIControlStateNormal];
[UIView setAnimationDuration:0];
button.alpha = 1;
[UIView commitAnimations];
You should not use beginAnimations:context: any more. To quote Apple's docs:
Use of this method is discouraged in iOS 4.0 and later. You should use
the block-based animation methods to specify your animations instead.
Use the new animateWithDuration family of methods instead. Specifically animateWithDuration:delay:options:animations:completion:. That method takes a delay parameter. You can issue a series of animation commands, each with a larger delay value. You can also chain animations by making the completion block of each animation trigger the next animation.
Edit: here is a link to a post where I show code that uses the completion method to trigger a sequence of animations one right after the other:
Scrolling the array of images using the CABasicAnimation
Try this thing in for loop
for(int btnIndex = 0; btnIndex < 7; btnIndex++) {
UIButton *button = [buttons objectAtIndex:0]; //get button reference from an array
[UIView beginAnimations: #"Fade In" context:nil];
button.alpha = 0;
[UIView setAnimationDelay:(0.1 * btnIndex)];
[button setTitle:#"helloworld" forState:UIControlStateNormal];
[UIView setAnimationDuration:0.1];
button.alpha = 1;
[UIView commitAnimations];
}
I'm having trouble changing my button text after animation.
Defined in my .h file
#property (weak, nonatomic) IBOutlet UIButton *btnCheck;
I call this method when i want to animate the button location change
- (void)updateViews {
if(checkedIn){
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionTransitionNone
animations:^{
btnClear.hidden = YES;
[btnCheck setFrame:CGRectMake(90, btnCheck.frame.origin.y, btnCheck.frame.size.width, btnCheck.frame.size.height)];
}
completion:nil];
}else{
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionTransitionNone
animations:^{
[btnCheck setFrame:CGRectMake(20, btnCheck.frame.origin.y, btnCheck.frame.size.width, btnCheck.frame.size.height)];
}
completion:^(BOOL finished){
btnClear.hidden = NO;
}];
}
checkedIn = !checkedIn;
}
That works perfect, pics included below for reference:
The problem is as soon as i try change the name of the button it jumps back to its original frame.
I change the button text in another method like this,
btnCheck.titleLabel.text = #"test";
And as soon as i change that the button jumps back
What's causing the button frame to be reset?
I've tried changing it before the animation,during and on completion. It animates to the new location and jumps back.
This will probabily not answer your question, but I'll show you a better way to change a frame when you want to change only one property in your frame
CGRect buttonFrame = [btnCheck frame];
buttonFrame.origin.x = newXOrigin;
[UIView animateWithDuration:0.3f
animations:^{
[btnCheck setFrame:buttonFrame];
}];
I prefer this method, because you don't have to create a new CGRect and copy paste all the unmodified frame attributes. Ow and you might wanna get the hidden assignment out of the animation block, since that cannot be animated
You should not change UIButton's title that way. Instead, try setting it's title using setTitle:forState: method. Try this, and let me know if this was helpful, Good Luck!
It was Autolayout that caused the problem. Disabled that and everything worked
I want to have the text of the button 'go away' when pressing a 'wrong answer' button.
In my problem-demo code, my project has two buttons, one with outlet 'myBtn' without any action, and one with TouchUpInside action. The action handler looks like this:
- (IBAction)goPressed:(UIButton*)sender {
//UILabel *lbl = self.myBtn.titleLabel;
UILabel *lbl = sender.titleLabel;
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
lbl.center = CGPointMake(lbl.center.x-60, lbl.center.y);
lbl.alpha = 0;
}
completion:nil];
}
I am trying to animate two properties: 'alpha' to go from 1 to 0, and position of text to move 60 points to the left.
If I uncomment the first "UILAbel" line and comment the second, then pressing the button runs a nice animation in the second button.
But if I leave the code as it appears, trying to animate the text of the pressed button itself, the alpha is animating fine, but the position is not changing.
Any help would be highly appreciated!
I have seen this kind of problem occurring on iOS7. Animations that worked fine inside an IBAction, don't work on iOS7. I had to move all my animation code to a different method and call the selector after a delay. Your code will work fine if you do this -
- (IBAction) goPressed:(UIButton*)sender {
[self performSelector:#selector(animateButton:) withObject:sender afterDelay:0.1];
}
- (void) animateButton:(UIButton *) button{
UILabel *lbl = button.titleLabel;
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
lbl.center = CGPointMake(lbl.center.x-60, lbl.center.y);
lbl.alpha = 0;
}
completion:nil];
}
Your problem is mixing up the UIButton with the UILabel.
The method parameter (UIButton*)sender is referencing a UIButton in this case. The reason UILabel *lbl = sender.titleLabel; doesn't work is because the sender is a UIButton reference. To access the label object, you must reference the the UILabel embedded in the UIButton via the hirarchy sender > UIButton > UILabel.
So the code you should use is:
UIButton *button = sender;
UILabel *lbl = sender.titleLabel;
[UIView animateWithDuration:1.0
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
lbl.center = CGPointMake(lbl.center.x-60, lbl.center.y);
lbl.alpha = 0;
}
completion:nil];
}
The reason why the code as it appears behaves strangly is because alpha is a property of both UIButtons and UILabels. So it will work even if you are incorrectly refenrencing the sender.
I have a tabbed application for iPad. On the first tab I have a separate menu with a pointer that highlights the currently active menu button. The pointer (a UIImage) is animated into position after tapping the button.
The problem is when you leave the highlighted image on any button that's not the original/default button and move to another tab in the application, then back again, the image has moved back to the default location. However, when you tap another button the image moves in the wrong direction. I think it's because the image moves back but the old coordinates are retained, or the other way around? It's a bit hit and miss and behaves differently testing in the simulator compared to directly on the iPad.
I think I need to start with absolute coordinates and stick with them throughout the process. But I can't get my head around how to achieve it with the CGAffine functions.
Here is my current code
- (IBAction)diplayForm1:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations];
}
- (IBAction)diplayForm2:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 80);
[UIView commitAnimations];
}
- (IBAction)diplayForm3:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 160);
[UIView commitAnimations];
}
EDIT - - -
#Mundi I tried the code you suggested. I'm assuming by newFrame you mean define a new UIImageView and property which I did in the .h (I called it 'sfh'), then #synthesize in the .m.
This is my code in the .m
-(void)viewWillAppear:(BOOL)animated {
// The "pointer" and the "old" frame are the same so I thought it pointless to do
// selectedFormHighlight.frame = selectedFormHighlight.frame;
// I tried anyway and also tried this and many other combinations
selectedFormHighlight.frame = sfh.frame;
}
-(void)viewDidAppear:(BOOL)animated {
[UIView beginAnimations:nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0,0,0,0);
}];
[UIView commitAnimations];
}
When I run this the pointer animates off the screen (top-left) and doesn't come back. I have tried every possible combination I can think of in viewWillAppear and viewDidAppear but it makes no difference. I tried removing the other lines in viewDidAppear and it still does the same thing.
I think you're suggesting to animate the pointer from the original (default) position when the user returns from another screen. I don't want that, it should simply remain in the same spot where they left it without animation. Or at least put it back there without them realizing it. The only animation is when they choose to tap a different button within that view.
The reason you are experiencing this is that the actual view object and its presentation layer get mixed up when the view is hidden and redisplayed. The solution is to change the view properties rather than animate the layers.
Thus, you do not need to use affine transforms. Just set the new position of the pointer view directly by changing its frame or center.
The more up-to-date format to do this is with block based animations:
[UIView animateWithDuration:0.5 animations:^{
pointer.frame = newFrame;
}];
Also, it seems very inefficient to have three methods just differing by one variable. You could just multiply the desired y position by 80.
Edit:
Another reason your button resets is because that is where it was put when the view is instantiated. Just set the position of your view in viewWillAppear of your view controller and then animate it to the new location in viewDidAppear.
I appreciate the help from #Mundi but couldn't get it to work that way. I eventually arrived at the following code.
// This initially sets the frame with a variable value that
// corresponds to the current child view
-(void)viewDidAppear:(BOOL)animated {
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}
// Button actions (converted to a single action instead of one for each button)
// This was done by referencing a tag added to each button (the button tags
// match the children's tags)
-(IBAction)goToForm:(id)sender {
UIButton *btn = (UIButton *)sender;
self.currentChild = self.formViewControllers[btn.tag];
[UIView beginAnimations.nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}];
[UIView commitAnimations];
sfh.frame = selectedFormHighlight.frame;
}
// This returns the 'y' (vertical center position) of the pointer
// frame for each of the child views
-(int)getY {
switch(_currentChild.view.tag) {
case 1:
return -2005;
break;
case 2:
return -1925;
break;
default:
return -1845;
break;
}
}