2013-02-28 13 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 – 2013-03-05 08:24:04

+1

一起工作我遇到了同樣的問題。我從來沒有能夠完全解決它,但以下幫助:1.如果值沒有改變,請不要設置currentPlaybackTime 2.不要在每次更改時設置currentPlaybackTime - 將值存儲在實例變量中,並通過一個定時器只有幾秒鐘。不過,它從來沒有像蘋果的滑蓋那樣流暢。 – EricS 2013-03-02 21:51:48

+0

你可以嘗試1.在設置_isScrubbing = NO和2之前添加一個微小的延遲。嘗試動畫價值變化,而不是直接設置_scrubberSlider.value – 2013-03-07 10:52:39

回答

2

記錄的很多之後 - 我發現UISlider部件有輕微的滯後更新本身 - 在1/10和3/10秒之間 - 導致跳躍。

我也改變了計時器的時間間隔爲0.5秒我還幫助添加savedValue可變內ScrubberSlider和BOOL hasFinishedMoving該更新,所以內部ScrubberSlider的beginTrackingWithTouchendTrackingWithTouch方法看起來像這樣:

-(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包括這些VARS和驗證:

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

也許你要對這個錯誤的方式。而不是試圖完全同步到軌道的時間(這會導致一些非常討厭的競爭條件,並且恰恰就是爲什麼當「拋出」而不是拖動,保持,然後釋放時,您的滑塊不能正確更新),請嘗試使用您作爲「勾號」的計時器。您可以將火速降低到例如每半秒一次,然後使用基本上與現在相同的邏輯,但不要試圖在-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

若要連續上滑塊拖更新定時器(不JUM 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有一個名爲addPeriodicTimeObserverForInterval:queue:usingBlock:方法與AVPlayer工作得很好嘗試一個小的修改。有一個AVPlayer demo,它有非常平滑的擦洗。我無法找到AVAudioPlayer的類似方法,但AVPlayer demo可能會給您一些關於如何實現平滑清理的想法。

問題的一部分是找到正確的計時器間隔,我確信AVPlayer demo可以幫助你。如果您必須使用計時器,則最好使用CADisplayLink(而不是NSTimer),因爲在處理更新屏幕顯示內容時更準確。

看起來您正在使用MPMusicPlayerController。如果您正在製作自己的控件,則最好使用AVAudioPlayerAVPlayer