2013-02-28 10 views
7

ミュージックトラックの再生の進行状況を表示し、トラック内でスクラブを可能にするカスタムスライダを構築しました。ミュージックアプリケーションのようなカスタムUISlider

両方の機能はうまく機能しますが、ドラッグが停止してスライダの位置が変更されると、わずかな遅延(および跳ね上がり)があります.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 
+0

http://www.cocoacontrols.com/search?utf8=%E2%9C%93&q=music –

+1

私は同じ問題を抱えていました。私はそれを完全に修正することはできませんでしたが、以下の助けとなりました。1.値が変更されていない場合はcurrentPlaybackTimeを設定しないでください。 2.変更ごとにcurrentPlaybackTimeを設定しないでください。 - インスタンス変数に値を保存し、タイマーは1秒に数回しかありません。 Appleのスライダーほど流動的ではなかった。 – EricS

+0

を試してみてください。1. _isScrubbing = NOと2)を設定する前に若干の遅延を追加してください。 _scrubberSlider.valueを直接設定するのではなく、値の変化をアニメーション化してみてください –

答えて

2

を - 私はUISliderコンポーネントがわずかに遅れを持っていたことが分かりましたそれ自身を更新する - 1/10秒から3/10秒の間に - ジャンプを引き起こした。

私はまた、0.5秒にタイマー間隔を変更し、ScrubberSlider内部beginTrackingWithTouchendTrackingWithTouch方法は次のようになりますので、私はまた、ScrubberSlidersavedValue変数を追加して更新し、BOOL hasFinishedMovingを助けた:

-(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; 
} 

とJUMなし(スライダードラッグで連続タイマーを更新するには

-(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

はおそらく、あなたはこれについて間違った道を進みました。トラックの時間に正確に同期しようとするのではなく(かなり厄介な競合状態に繋がり、スライダがドラッグ、ホールド、リリースされるのではなく「スロー」されたときに正しく更新されない理由です)あなたが持っているタイマーを「ダニ」として使用してください。たとえば、1秒に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]; 
} 
1

:これらのVARSと検証を含めるようにupdatePositionを変更pyの動き)、scheduledTimerWithTimeIntervalの値を最小限にする必要があります。だから、あなたのタイマ値は、このようなものである場合: -

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

はscheduledTimerWithTimeInterval値

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



-(void)updateElapsedTime { 

    //do your stuffs here 
} 
2

AVFoundation frameworkを最小限に抑えることによって、小さな変更を試してみてくださいAVPlayerと非常によく動作しますaddPeriodicTimeObserverForInterval:queue:usingBlock:と呼ばれる方法があります。非常に滑らかなスクラブを持つAVPlayer demoがあります。私はAVAudioPlayerのための同様の方法を見つけることができませんでしたが、おそらくAVPlayer demoは滑らかなスクラブを達成する方法のいくつかのアイデアを与えるでしょう。

問題の一部は正しいタイマー間隔を見つけることです。これはAVPlayer demoがお手伝いできると確信しています。タイマーを使用する必要がある場合は、画面上の何かの表示を更新する場合には、より正確なので、(NSTimerの代わりに)を使用する方が良いでしょう。

MPMusicPlayerControllerを使用しているようです。独自のコントロールを作成する場合は、AVAudioPlayerまたはAVPlayerを使用する方がよいでしょう。