Matt Gallagher 的 AudioStream:如何处理视图控制器弹出
嗯,这是我的第一篇文章 - 在这个网站、个人博客和 iPhoneDevSDK 论坛之间,我一直能够自己解决问题。我已经为 iOS 开发了大约五个月的时间,并且从未向 Google 之外寻求过帮助。但这一次,我陷入了困境。
我已经成功地将 Matt Galagher 的 AudioStream 类(必须删除链接,因为 SO 假设我的帖子是垃圾邮件)实现到我的应用程序中,并且我很高兴地报告它工作得非常好。我在一个视图控制器中使用它,该控制器实际上是 TabBar 应用程序中父视图的子视图。事实上,我的实现与 Matt 的示例中的实现没有太大不同 ——我只改变了一些 UI 元素。
到目前为止,我一直在使用 viewDidDisappear
在用户切换到另一个选项卡时停止流媒体。但最近我决定,只要应用程序正在运行,就可以让音频流播放,无论顶部的视图是什么。这是一个非常简单的更改,现在无论我在应用程序中的哪个位置,我的音频流都会继续播放。
视图控制器的工作原理如下:
- 我有一个“播放”按钮和一个“停止”按钮位于同一位置,但“停止”按钮隐藏。
- 当用户按下“播放”按钮时,“播放”按钮会隐藏,显示一个 UIActivityIndicatorView(“[streamer isWaiting]”)。
- 当流开始播放(“[streamer isPlaying]”)时,“UIActivityIndicatorView”将隐藏,并出现“停止”按钮
- 另外,当“[streamer isPlaying]”时,我在导航提示中显示经过的时间(mm:ss),每秒更新一次以上
所有内容每次都完美工作。如果我离开视图(通过弹出到父视图或导航到另一个选项卡),音频将继续播放,这正是我想要的。但是当我返回视图时,UI 看起来好像我还没有启动它(“播放”按钮可见,“导航提示”隐藏)。如果我按下播放按钮,我会听到第二个流与第一个流同时播放。
updateProgress
似乎已经停止(因为 self.navigationItem.prompt
不再可见),我猜测 playbackStateChanged
已经“死了” “(不知道如何描述)。
我花了几个小时筛选 AudioStreamer 类,试图找出如何保持对流的控制,但我已经筋疲力尽了。我希望有人能够告诉我我错过了什么。
正如我之前所说,我的视图控制器与示例几乎相同(请参阅上面的超链接,因为 SO 仍然假设我是垃圾邮件发送者),只有一些与 UI 相关的更改。
我想简短的问题是这样的:有没有人能够实现 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
Well, this is my very first post ever -- between this website, personal blogs, and iPhoneDevSDK forums, I have always been able to figure things out on my own. I've been developing for iOS for about five months and have never asked for help outside of Google. But this time, I'm stuck.
I have successfully implemented Matt Galagher's AudioStream class (had to remove link because SO assumes my post is spam) into my app, and I'm happy to report that it works beautifully. I'm using it in one view controller, which is actually a child to a parent view in a TabBar app. In fact, my implementation isn't much different than that in Matt's example -- I only changed some UI elements.
Up to this point, I'd been using viewDidDisappear
to stop the streamer when the user switches to another tab. But recently I decided it would be slick to let the audio stream play as long as the app is running, no matter what view is on top. That was a very simple change, and now my audio stream continues to play no matter where I am in the app.
Here's how the view controller works:
- I have a Play button and a Stop button laying in the same spot, with the Stop button hidden.
- When the user presses the Play button, the Play button hides, revealing a UIActivityIndicatorView (`[streamer isWaiting]`)
- When the stream begins playing (`[streamer isPlaying]`), the `UIActivityIndicatorView` is hidden, and a Stop button appears
- Also, while `[streamer isPlaying]`, I'm displaying an elapsed time (mm:ss) inside the Navigation Prompt, which updates each second
All of the above work perfectly every time. If I leave the view (either by popping to the parent view, or by navigating to another tab), the audio will stay playing, which is exactly what I want. But when I return to the view, the UI appears as though I haven't started it yet (Play button visible, Navigation Prompt is hidden). If I press the play button, then I hear a second stream playing simultaneously with the first.
The updateProgress
seems to have stopped (as self.navigationItem.prompt
is no longer visible), and I'm guessing the playbackStateChanged
is "dead" (not sure how to describe it).
I've spent hours sifting through the AudioStreamer class trying to figure out how to maintain control of the stream, but I'm exhausted. I'm hoping someone might be able to tell me what I'm missing.
As I said earlier, my view controller is nearly identical to the example (see hyperlink above, as SO still assumes I'm a spammer), with only a few UI-related changes.
I guess the short question would be this: has anyone been able to implement the AudioSTreamer class, pop its view, then come back and be able to view elapsed time or stop the stream?
EDIT: below is my view controller implementing 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
事实证明,我只需要重新思考如何分配和推送视图。 “更多”选项卡显示一个表格视图,每个单元格代表一个子视图,当选择该单元格时将推送该子视图。我更改了此视图以分配
viewDidLoad
内的所有子视图,然后只需在didSelectRowAtIndexPath
内执行推送,并在dealloc
中释放子视图。这完美解决了我的问题。感谢杰森·可可让一切顺利进行。
Turns out that I just needed to re-think how I'm allocating and pushing views. The "More" tab displays a table view, and each cell represents a child view that will be pushed when that cell is selected. I changed this view to alloc all child views inside
viewDidLoad
, then simply perform the push insidedidSelectRowAtIndexPath
, and release the child views indealloc
.This solves my problem perfectly. Thanks to Jason Coco for getting the ball rolling.