AudioStream Мэтта Галлахера: как работать с контроллером представления - PullRequest
8 голосов
/ 20 января 2011

Ну, это мой самый первый пост в истории - между этим сайтом, личными блогами и форумами iPhoneDevSDK я всегда мог сам разобраться. Я разрабатывал для iOS около пяти месяцев и никогда не обращался за помощью за пределами Google. Но на этот раз я застрял.

Я успешно внедрил класс AudioStream Мэтта Галагера (пришлось удалить ссылку, поскольку SO предполагает, что мой пост является спамом) в мое приложение, и я рад сообщить, что он прекрасно работает. Я использую его в одном контроллере представления, который на самом деле является дочерним по отношению к родительскому представлению в приложении TabBar. Фактически, моя реализация не сильно отличается от реализации в примере Мэтта - я изменил только некоторые элементы пользовательского интерфейса.

До этого момента я использовал viewDidDisappear, чтобы остановить стример, когда пользователь переключается на другую вкладку. Но недавно я решил, что было бы неплохо позволить аудиопотоку воспроизводиться, пока приложение работает, независимо от того, какой вид сверху. Это было очень простое изменение, и теперь мой аудиопоток продолжает воспроизводиться независимо от того, где я нахожусь в приложении.

Вот как работает контроллер вида:

  1. У меня кнопка Play и кнопка Stop лежат в одном месте со скрытой кнопкой Stop.
  2. Когда пользователь нажимает кнопку Play, кнопка Play скрывается, открывая UIActivityIndicatorView (`[streamer isWaiting]`)
  3. Когда поток начинает воспроизводиться (`[streamer isPlaying]`), `UIActivityIndicatorView` скрывается, и появляется кнопка Stop
  4. Кроме того, пока `[streamer isPlaying]`, я отображаю прошедшее время (мм: сс) в окне навигации, которое обновляется каждую секунду

Все вышеперечисленное прекрасно работает каждый раз. Если я покину вид (либо нажав на родительский вид, либо перейдя на другую вкладку), звук продолжит воспроизводиться, и это именно то, что я хочу. Но когда я возвращаюсь к представлению, пользовательский интерфейс выглядит так, как будто я его еще не запустил (видна кнопка «Воспроизвести», подсказка навигации скрыта). Если я нажимаю кнопку воспроизведения, я слышу второй поток, воспроизводимый одновременно с первым.

Кажется, что updateProgress остановился (так как self.navigationItem.prompt больше не виден), и я предполагаю, что playbackStateChanged "мертв" (не знаю, как это описать).

Я часами разбирался в классе AudioStreamer, пытаясь выяснить, как сохранить контроль над потоком, но я измотан. Я надеюсь, что кто-то сможет сказать мне, что мне не хватает.

Как я уже говорил ранее, мой контроллер представления почти идентичен примеру (см. Гиперссылку выше, поскольку SO по-прежнему предполагает, что я спамер), только с некоторыми изменениями, связанными с пользовательским интерфейсом.

Полагаю, короткий вопрос был бы таков: смог ли кто-нибудь реализовать класс AudioSTreamer, открыть его представление, затем вернуться и иметь возможность просматривать истекшее время или остановить поток?

РЕДАКТИРОВАТЬ: ниже мой контроллер представления, реализующий AudioStreamer

.h

#import <UIKit/UIKit.h>
#import <QuartzCore/CoreAnimation.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CFNetwork/CFNetwork.h>

@class AudioStreamer;


@interface RadioViewController : UIViewController
{
    IBOutlet UIButton *playButton;
    IBOutlet UIButton *stopButton;
    IBOutlet UIActivityIndicatorView *waitIndicator;

    AudioStreamer *streamer;
    NSTimer *progressUpdateTimer;

    BOOL shouldAutoStop;
}

- (IBAction)play;
- (IBAction)stop;

- (void)createStreamer;
- (void)destroyStreamer;
- (void)updateProgress:(NSTimer *)aNotification;
- (void)checkWiFi;

@end

.m

#import "AudioStreamer.h"
#import "ServerCheck.h"
#import "RadioViewController.h"

@implementation RadioViewController


- (void)viewDidLoad
{
    [[self navigationItem] setTitle:@"WXK33 162.550"];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkWiFi) name:UIApplicationDidBecomeActiveNotification object:nil];

    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated
{   
    [self performSelector:@selector(checkWiFi)];

    [super viewDidAppear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
    //[self performSelector:@selector(stop)];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];

    [super viewDidDisappear:animated];
}


- (void)createStreamer
{
    if ([ServerCheck serverReachable:@"audiostream.wunderground.com"])
    {
        if (streamer)
        {
            return;
        }

        [self destroyStreamer];

        NSURL *url = [NSURL URLWithString:@"http://audiostream.wunderground.com/MaffooClock/San_Angelo.mp3"];
        streamer = [[AudioStreamer alloc] initWithURL:url];

        progressUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateProgress:) userInfo:nil repeats:YES];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackStateChanged:) name:ASStatusChangedNotification object:streamer];

    }
    else
    {
        [[self navigationController] popViewControllerAnimated:YES];
    }
}

- (IBAction)play
{
    [self createStreamer];
    [waitIndicator startAnimating];
    [streamer start];

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}

- (IBAction)stop
{
    self.navigationItem.prompt = nil;
    [streamer stop];

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}


- (void)playbackStateChanged:(NSNotification *)aNotification
{
    if ([streamer isWaiting])
    {
        [playButton setHidden:YES];
        [stopButton setHidden:YES];
        [waitIndicator startAnimating];
    }
    else if ([streamer isPlaying])
    {   
        [playButton setHidden:YES];
        [stopButton setHidden:NO];
        [waitIndicator stopAnimating];

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
    }
    else if ([streamer isIdle])
    {
        [self destroyStreamer];

        [playButton setHidden:NO];
        [stopButton setHidden:YES];
        [waitIndicator stopAnimating];
    }
}


- (void)updateProgress:(NSTimer *)updatedTimer
{
    if (streamer.bitRate > 0.0)
    {
        double progress = streamer.progress;

        double minutes = floor(progress/60);
        double seconds = trunc(progress - (minutes * 60));

        self.navigationItem.prompt = [NSString stringWithFormat:@"Elapsed: %02.0f:%02.0f",minutes,seconds];;

        if (shouldAutoStop && progress > 600)
            [self performSelector:@selector(stop)];
    }
    else
    {
        self.navigationItem.prompt = nil;
    }
}

- (void) checkWiFi
{
    if (![ServerCheck wifiAvailable])
    {
        LogInfo(@"No Wi-Fi");

        NSString * messageTitle = @"Notice";
        NSString * messageText = @"It appears that you do not have an active Wi-Fi connection.  Listening to streaming audio via cellular data may incurr additional data charges.  Streaming will automatically stop after 10 minutes.";

        UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:messageTitle message:messageText delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];

        [errorAlert show];
        [errorAlert release];

        shouldAutoStop = YES;
    }
    else
        shouldAutoStop = NO;    
}


- (void)destroyStreamer
{
    if (streamer)
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:ASStatusChangedNotification object:streamer];
        [progressUpdateTimer invalidate];
        progressUpdateTimer = nil;

        [streamer stop];
        [streamer release], streamer = nil;
    }
}


- (void)dealloc
{
    [self destroyStreamer];
    if (progressUpdateTimer)
    {
        [progressUpdateTimer invalidate], progressUpdateTimer = nil;
    }
    [super dealloc];
}

@end

1 Ответ

0 голосов
/ 20 января 2011

Оказывается, мне просто нужно было переосмыслить то, как я распределяю и продвигаю взгляды.На вкладке «Дополнительно» отображается табличное представление, и каждая ячейка представляет дочернее представление, которое будет выдвигаться при выборе этой ячейки.Я изменил это представление, чтобы выделить все дочерние представления внутри viewDidLoad, затем просто выполнить push внутри didSelectRowAtIndexPath и освободить дочерние представления в dealloc.

Это решает мою проблему отлично.Спасибо Джейсону Коко за то, что он перевёл мяч.

...