2013-02-28 64 views
7

Tôi đã tạo thanh trượt tùy chỉnh để hiển thị tiến trình phát nhạc và cho phép chà trong bản nhạc.UISlider tùy chỉnh như ứng dụng Nhạc

Cả hai chức năng đều tốt, nhưng có một độ trễ nhỏ (và chuyển động nhanh) khi kéo đã dừng và thanh trượt được định vị lại - Thanh trượt ứng dụng Apple Music liền mạch.

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

Tục Slider

@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

Kiểm tra các thành phần này, sẽ dễ dàng hơn khi làm việc với http://www.cocoacontrols.com/search?utf8=%E2%9C%93&q=music –

+1

Tôi gặp vấn đề tương tự. Tôi đã không bao giờ có thể sửa chữa nó hoàn toàn, nhưng sau đây đã giúp: 1. Không đặt currentPlaybackTime nếu giá trị không thay đổi 2. Không đặt currentPlaybackTime mọi thay đổi - lưu trữ giá trị trong một biến mẫu và đặt nó qua bộ đếm thời gian chỉ vài lần trong một giây. Nó không bao giờ là chất lỏng như thanh trượt của Apple, mặc dù. – EricS

+0

bạn có thể thử 1. thêm một chút chậm trễ trước khi thiết lập _isScrubbing = NO và 2). thử làm thay đổi giá trị thay vì đặt _scrubberSlider.value trực tiếp –

Trả lời

2

Sau rất nhiều logging - Tôi thấy rằng các thành phần UISlider đã có một độ trễ nhẹ cập nhật chính nó - trong khoảng từ 1/10 đến 3/10 giây của một giây - gây ra sự nhảy vọt.

Tôi cũng đã thay đổi khoảng thời gian hẹn giờ đến 0,5 giây và tôi cũng giúp cập nhật với việc thêm một biến savedValue bên ScrubberSlider và một BOOL hasFinishedMoving, vì vậy beginTrackingWithTouchendTrackingWithTouch phương pháp bên ScrubberSlider trông như thế này:

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

và thay đổi updatePosition để bao gồm các VAR và kiểm chứng thực:

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

Có lẽ bạn đang đi về vấn đề này một cách sai lầm. Thay vì cố gắng được đồng bộ hóa chính xác với thời gian của bản nhạc (dẫn đến một số điều kiện đua khá khó chịu và chính xác là tại sao thanh trượt của bạn không cập nhật đúng khi "ném" thay vì kéo, giữ, sau đó thả ra), hãy thử sử dụng bộ hẹn giờ bạn có như một "đánh dấu". Bạn có thể giảm tốc độ cháy xuống, cứ mỗi nửa giây, sau đó sử dụng cơ bản cùng một logic bạn có, nhưng thay vì cố gắng lấy giá trị phân số chính xác trong -updatePosition, bạn "đánh dấu" thanh trượt về phía trước.

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

Đó không phải là hoàn hảo, nhưng sau đó một lần nữa, đó là hiệu quả hơn, liền mạch hơn là cố gắng để đồng bộ hóa chính mình để thời gian của một ca khúc nhạc với NSTimer (có thể không chính xác disgustingly cho phong trào chính xác). Điều này cũng đòi hỏi giá trị tối đa của thanh trượt của bạn tương đương với giá trị tối đa của ca khúc, vì vậy một phương pháp ví dụ để bắt đầu phát lại sẽ bao gồm một cái gì đó như:

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

để cập nhật giờ liên tục trên thanh trượt kéo (không Jum py movement) chúng ta nên giảm thiểu giá trị của scheduleTimerWithTimeInterval. Vì vậy, Nếu giá trị đếm thời gian của bạn là một cái gì đó như thế này: -

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

Hãy thử một sửa đổi nhỏ bằng cách giảm thiểu giá trị scheduledTimerWithTimeInterval

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



-(void)updateElapsedTime { 

    //do your stuffs here 
} 
2

Các AVFoundation framework có một phương pháp gọi là addPeriodicTimeObserverForInterval:queue:usingBlock: rằng hoạt động rất tốt với AVPlayer. Có một AVPlayer demo có độ chà rất mịn. Tôi đã không thể tìm thấy một phương pháp tương tự cho AVAudioPlayer, nhưng có lẽ AVPlayer demo sẽ cung cấp cho bạn một số ý tưởng về cách đạt được chà mịn.

Một phần của sự cố là tìm khoảng thời gian bộ đếm đúng, mà tôi chắc chắn rằng AVPlayer demo có thể giúp bạn. Nếu bạn phải sử dụng bộ hẹn giờ, có thể tốt hơn nên sử dụng một số CADisplayLink (thay vì NSTimer) vì điều đó chính xác hơn khi xử lý việc cập nhật hiển thị nội dung nào đó trên màn hình.

Có vẻ như bạn đang sử dụng MPMusicPlayerController. Nếu bạn đang thực hiện các điều khiển của riêng mình, có thể bạn nên sử dụng số điện thoại AVAudioPlayer hoặc AVPlayer.