Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animation in UISlider is skipped on iOS 7

I have a slider that is functioning as 2 sliders, according to the audio played - when 1 type of audio (some kind of vocal guidance) is disabled, a music is played, and the slider controls the music's volume.

When changing roles, the slider changes positions, according the its role (guidance - upper in the view, music - lower) and adjusts the its value (the volume) to a saved volume value for that type of sound (guidance sound or music sound).

The type of effect I was looking for was -

  • Move the slider to its new location, using [UIView animateWithDuration]
  • When the slider reaches its location, change its value to reflect the volume, again, using [UIView animateWithDuration].

First, I wrote it like that -

[UIView animateWithDuration:0.3
    animations:^{self.volumeSlider.frame = sliderFrame;}
    completion:^(BOOL finished){
        [UIView animateWithDuration:0.3
            animations:^{self.volumeSlider.value = newValue;}
    ];
}];

Which worked very well in the iOS 6 simulator (using Xcode 4.6.3), but when changing to my phone, running iOS 7, the slider changed its position and then the slider's value jumped to the new value. The same problem occurred again when running in the iOS 7 simulator that comes with Xcode 5, so I assume it's an iOS 7 problem.

I did some experiments, all with different results:

  • I tried setting the volume using '[UIView animateWithDuration:0.3 delay:0.3 options: animations: completion:]', meaning, not in the completion part, but the same thing happend.
  • When putting just the 2 animations one after another (each as separate animation, each without a delay, one after another) the result will vary according to the order of the animations.

    [UIView animateWithDuration:0.3 animations:^{self.volumeSlider.value = newValue;}];
    [UIView animateWithDuration:0.3 animations:^{self.volumeSlider.frame = sliderFrame;}];
    

Will move both the slider and its value together, both animated, while

    [UIView animateWithDuration:0.3
                 animations:^{self.volumeSlider.frame = sliderFrame;}];
    [UIView animateWithDuration:0.3
                 animations:^{self.volumeSlider.value = newValue;}];

Will move the slider's position, and then change its value without animation.

  • I tried calling the 2nd animation through

    [self performSelector:@selector(animateVolume:) withObject:nil afterDelay:0.3];
    

And again - the slider moved, and then the value changed at once.

WHY, OH WHY?? If it helps, this is the description of the slider, BEFORE the first animation -

<UISlider: 0xcc860d0; frame = (23 156; 276 35); autoresize = RM+BM;
layer = <CALayer: 0xcc86a10>; value: 1.000000>

And After the first animation ended -

<UISlider: 0xcc860d0; frame = (23 78; 276 35); autoresize = RM+BM; 
animations = { position=<CABasicAnimation: 0xbce5390>; }; 
layer = <CALayer: 0xcc86a10>; value: 0.000000>

Notice the animations section, that shouldn't be there by now (the description is logged from [self animateVolume] which is called with a delay of .3 seconds).

I know its a weird problem, but I would very much appreciate help with it.

Thank :) Dan

UPDATE

As Christopher Mann suggested, changing the value in the UIView:animationWithDuration is not the official way of using it, and the correct way would be to use UISlider's setValue:animated.

However, for the people who will encounter such problem in the future - it seems that iOS 7 has some difficulties with that method, such that it is not animated in some cases (I think it will not be animated if the project was started in Xcode < 5). The problem and its solution are described here.

My code that solved this problem is:

[UIView animateWithDuration:0.3
                 animations:^{self.volumeSlider.frame = sliderFrame;}
                 completion:^(BOOL finished){
                     [UIView animateWithDuration:1.0 animations:^{
                         [self.volumeSlider setValue:newValue animated:YES];
                     }];
                     currentSliderMode = mode;
}];
like image 820
Dan Avatar asked Oct 22 '13 17:10

Dan


1 Answers

If you want to animate the change in slider value you should use setValue:animated: instead of setting .value directly. Changing volumeSlider.value inside the UIView animation block is likely interfering the the animation.

like image 131
Christopher Mann Avatar answered Nov 15 '22 08:11

Christopher Mann