【问题标题】:Custom UISlider like the Music app自定义 UISlider,如音乐应用
【发布时间】:2013-02-28 07:40:28
【问题描述】:

我已经构建了一个自定义滑块来显示音乐曲目的播放进度并允许在曲目内进行擦洗。

两者功能都很好,但是一旦停止拖动并重新定位滑块,就会有轻微的延迟(和跳跃的运动) - Apple Music 应用程序滑块是无缝的。

_scrubberSlider = [[ScrubberSlider alloc] initWithFrame:CGRectMake(0, 0, 300, 30)];
_scrubberSlider.continuous = YES;
_scrubberSlider.maximumValue = 1.0f;
_scrubberSlider.minimumValue = 0.0f;
 [_scrubberSlider addTarget:self action:@selector(handleSliderMove:) forControlEvents:UIControlEventValueChanged];

- (void) handleSliderMove:(UISlider*)sender
{
    CGFloat currentPlayback = sender.value * [[_theMusicPlayer.nowPlayingItem valueForKey:MPMediaItemPropertyPlaybackDuration] floatValue];
    _theMusicPlayer.currentPlaybackTime = currentPlayback;
}

-(void) handleTrackTime
{
    if (!trackTimer)
    {
    NSNumber *playBackTime = [NSNumber numberWithFloat: _musicPlayer.currentPlaybackTime];
    trackTimer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(timeIntervalFinished:) userInfo:@{@"playbackTime" : playBackTime} repeats:YES];
    }
}

-(void) timeIntervalFinished:(NSTimer*)sender
{
     [self playbackTimeUpdated:_musicPlayer.currentPlaybackTime];
}

-(void) playbackTimeUpdated:(CGFloat)playbackTime 
{
    // Update time label
    [self updatePosition];
}

-(void) updatePosition
{
   if (!_scrubberSlider.isScrubbing)
   {
   CGFloat percent = _theMusicPlayer.currentPlaybackTime / [[_theMusicPlayer.nowPlayingItem valueForKey:MPMediaItemPropertyPlaybackDuration] floatValue];
   _scrubberSlider.value = percent;
   }
}

自定义滑块

@interface ScrubberSlider : UISlider

@property(nonatomic) BOOL isScrubbing;

@end

@implementation ScrubberSlider

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
    }
    return self;
}

-(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    [super beginTrackingWithTouch:touch withEvent:event];

    _isScrubbing = YES;

    return YES;
}

- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    [super endTrackingWithTouch:touch withEvent:event];

    _isScrubbing = NO;
}

@end

【问题讨论】:

  • 我遇到了同样的问题。我永远无法完全修复它,但以下方法有所帮助: 1. 如果值没有更改,请不要设置 currentPlaybackTime 2. 不要在每次更改时设置 currentPlaybackTime - 将值存储在实例变量中并通过计时器每秒只有几次。不过,它从来没有像 Apple 的滑块那样流畅。
  • 看看这个组件,使用cocoacontrols.com/search?utf8=%E2%9C%93&q=music会更容易
  • 您可以尝试 1. 在设置 _isScrubbing = NO 和 2) 之前添加一点延迟。尝试为值更改设置动画,而不是直接设置 _scrubberSlider.value
  • 我没有明白你的意思...你能解释一下吗?

标签: iphone objective-c ios6 uislider


【解决方案1】:

也许你以错误的方式解决这个问题。与其尝试与轨道的时间完全同步(这会导致一些非常讨厌的比赛条件,这正是为什么您的滑块在“抛出”而不是拖动、按住然后释放时不能正确更新的原因),而是尝试使用您拥有的计时器作为“滴答”。您可以将射速降低到每半秒一次,然后使用与现在基本相同的逻辑,但不要尝试在-updatePosition 中获得精确的小数值,而是向前“勾选”滑块.

-(void) updatePosition
{
   if (!_scrubberSlider.isScrubbing)
   {
       _scrubberSlider.value += .5f;
   }
}

这并不完美,但话又说回来,它比尝试使用 NSTimer 将自己与音乐曲目的时间同步(这对于精确的动作可能会非常不准确)更加有效和无缝。这还要求滑块的最大值等于轨道的最大值,因此开始播放的示例方法包括:

- (IBAction)startPlayback:(id)sender {
    //... Handle however you wish to play the track
    _scrubberSlider.maximumValue = [[_theMusicPlayer.nowPlayingItem valueForKey:MPMediaItemPropertyPlaybackDuration] floatValue];
    [self handleTrackTime];
}

【讨论】:

    【解决方案2】:

    在大量记录之后 - 我发现 UISlider 组件在更新自身时有轻微的延迟 - 在 1/10 到 3/10 秒之间 - 这导致了跳跃。

    我还将计时器间隔更改为 0.5 秒,我还通过在 ScrubberSlider 和 BOOL hasFinishedMoving 中添加了一个 savedValue 变量来帮助更新,所以 ScrubberSlider 中的 beginTrackingWithTouch endTrackingWithTouch 方法看起来像这样:

    -(BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
    {
        [super beginTrackingWithTouch:touch withEvent:event];
    
        _hasFinishedMoving = NO;
    
        return YES;
    }
    
    - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
    {
        [super endTrackingWithTouch:touch withEvent:event];
    
        _savedValue = self.value;
    
       _hasFinishedMoving = YES;
    }
    

    并更改了 updatePosition 以包含这些变量和验证:

    -(void) updatePosition
    {
       if (!_scrubberSlider.tracking) // this is a UISlider BOOL
       {
           if (_scrubberSlider.hasFinishedMoving)
           {
               _scrubberSlider.value = _scrubberSlider.savedValue;
               _scrubberSlider.hasFinishedMoving = NO;
           }
           else
           {
               CGFloat percent = _theMusicPlayer.currentPlaybackTime / [[_theMusicPlayer.nowPlayingItem valueForKey:MPMediaItemPropertyPlaybackDuration] floatValue];
    
               _scrubberSlider.value = percent;
           }
       }
    }
    

    【讨论】:

      【解决方案3】:

      AVFoundation framework 有一个名为addPeriodicTimeObserverForInterval:queue:usingBlock: 的方法,可以很好地与AVPlayer 配合使用。有一个AVPlayer demo 具有非常平滑的擦洗效果。我无法为AVAudioPlayer 找到类似的方法,但也许AVPlayer demo 会给你一些关于如何实现平滑擦洗的想法。

      部分问题在于找到正确的计时器间隔,我相信AVPlayer demo 可以帮助您。如果您必须使用计时器,最好使用CADisplayLink(而不是NSTimer),因为在更新屏幕上显示的内容时更准确。

      您似乎正在使用MPMusicPlayerController。如果您要制作自己的控件,最好使用AVAudioPlayer 或AVPlayer。

      【讨论】:

        【解决方案4】:

        要在滑块拖动时连续更新计时器(不跳动),我们应该最小化 scheduleTimerWithTimeInterval 的值。所以,如果你的计时器值是这样的:-

        timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateElapsedTime) userInfo:nil repeats:YES];
        

        尝试通过最小化 scheduleTimerWithTimeInterval 值进行小修改

        timer=[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(updateElapsedTime) userInfo:nil repeats:YES];
        
        
        
        -(void)updateElapsedTime {
        
             //do your stuffs here
        }
        

        【讨论】:

          猜你喜欢
          • 2016-08-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-06
          • 1970-01-01
          • 1970-01-01
          • 2015-10-13
          • 1970-01-01
          相关资源
          最近更新 更多