- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个 pageViewController (UIPageViewController),它有一个 subview Controller (名为 soundPlayer)。声音播放器有静态 AVAudioPLayer *player
在页面之间导航时,我们必须有播放器(单例模式)。所以如果我在页面之间导航而不播放声音,最后从这些 View Controller (导航 Controller )返回,soundPlayer 将被释放。但是当我播放声音并在页面之间导航时,每翻一页,新的 soundPlayer 都不会释放,并且会创建一个新的 soundPlayer !
在每次翻页播放声音之前,只有我们在soundPlayerViewController上,但是当我们每次翻页播放声音时,soundPlayerViewController并没有释放,活体对象增加了。
播放声音之前
播放声音后
忽略我的真实应用程序中的那些泄漏,我没有它们
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@class AppScrollView;
@interface SoundPlayerViewController : UIViewController <AVAudioPlayerDelegate>{
BOOL paused;
BOOL inBackground;
NSTimer *updateTimer;
UIImage *playBtnBG;
UIImage *pauseBtnBG;
UIImage *bookmarkedBtnBG;
}
@property (weak, nonatomic) IBOutlet UILabel *duration;
@property (weak, nonatomic) IBOutlet UILabel *currentTime;
@property (weak, nonatomic) IBOutlet UISlider *progressBar;
@property (nonatomic, strong) NSTimer *updateTimer;
@property (nonatomic, assign) BOOL inBackground;
@property (weak, nonatomic) IBOutlet UIButton *playButton;
- (IBAction)playButtonPressed:(UIButton *)sender;
- (IBAction)progressSliderMoved:(UISlider *)sender;
- (void)updateViewForPlayerState:(AVAudioPlayer *)player;
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)player;
- (void)updateViewForPlayerInfo:(AVAudioPlayer *)player;
- (void)updateCurrentTimeForPlayer:(AVAudioPlayer *)player;
- (void)registerForBackgroundNotifications;
- (void)updateBookmarkButton;
- (void)stopSoundPlayer;
@end
SoundPlayerViewController.m
#import "SoundPlayerViewController.h"
@interface SoundPlayerViewController ()
- (void)customizeAppearance;
@end
@implementation SoundPlayerViewController
static AVAudioPlayer *soundPlayer;
@synthesize numberFormatter;
@synthesize duration;
@synthesize currentTime;
@synthesize progressBar;
@synthesize updateTimer;
@synthesize inBackground;
@synthesize playButton;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self customizeAppearance];
paused = true;
[self registerForBackgroundNotifications];
NSError *error = nil;
NSString *fileName = @"sample";
NSString *fileType = @"m4a";
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:fileName ofType:fileType];
NSURL *soundURL = [NSURL fileURLWithPath:soundFilePath];
//soundPalyer is Static so there is one for all of instance objects of this class.
if (!soundPlayer) {
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:&error];
if (soundPlayer) {
[self updateViewForPlayerState:soundPlayer];
[self updateViewForPlayerInfo:soundPlayer];
soundPlayer.numberOfLoops = 0;
soundPlayer.delegate = self;
updateTimer = nil;
}
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//if you pause soundPlayer and curl page you will notice soundPlayer won't update so
//I update sound player
[self updateViewForPlayerState:soundPlayer];
}
- (void)viewWillDisappear:(BOOL)animated
{
[self updateViewForPlayerState:soundPlayer];
[super viewWillDisappear:animated];
}
#pragma mark - my custom functions
- (void)customizeAppearance
{
//change slider appearacne
UIImage *minImage = [[UIImage imageNamed:@"sliderMinTrack"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
UIImage *maxImage = [[UIImage imageNamed:@"sliderMaxTrack"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 9)];
UIImage *thumbImage = [UIImage imageNamed:@"sliderThumb"];
[progressBar setMaximumTrackImage:maxImage
forState:UIControlStateNormal];
[progressBar setMinimumTrackImage:minImage
forState:UIControlStateNormal];
[[UISlider appearance] setThumbImage:thumbImage
forState:UIControlStateNormal];
[[UISlider appearance] setThumbImage:thumbImage
forState:UIControlStateHighlighted];
[playButton setImage:playBtnBG forState:UIControlStateNormal];
}
- (void)pausePlaybackForPlayer:(AVAudioPlayer *)player
{
[player pause];
[self updateViewForPlayerState:player];
}
- (void)startPlaybackForPlayer:(AVAudioPlayer *)player
{
//I must add paused = false here, else if I stop playing and bring front the app from background, sound player continue playing.
paused = false;
player.enableRate = YES;
[player prepareToPlay];
[player play];
[self updateViewForPlayerState:player];
}
- (IBAction)playButtonPressed:(UIButton *)sender
{
if (soundPlayer.playing == YES) {
[self pausePlaybackForPlayer:soundPlayer];
//I use this flag to control when the audio should be played
paused = true;
} else {
[self startPlaybackForPlayer:soundPlayer];
}
}
- (IBAction)progressSliderMoved:(UISlider *)sender
{
soundPlayer.currentTime = sender.value;
progressBar.maximumValue = soundPlayer.duration;
[self updateViewForPlayerState:soundPlayer];
}
- (void)stopSoundPlayer
{
if (self.mailPickerIsPresented) {
if ([soundPlayer isPlaying]) {
self.mailPickerhasPausedPlayback = true;
[self pausePlaybackForPlayer:soundPlayer];
}
}
else
{
[soundPlayer stop];
soundPlayer = nil;
}
}
- (void)updateViewForPlayerState:(AVAudioPlayer *)player
{
if (updateTimer)
[updateTimer invalidate];
if (player.playing) {
if(pauseBtnBG)
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(updateCurrentTime) userInfo:player repeats:YES];
} else {
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
//I called this function below to set currentTimeLabel=0 after finishig playing
//[self updateCurrentTimeForPlayer:soundPlayer];
updateTimer = nil;
}
}
- (void)updateViewForPlayerStateInBackground:(AVAudioPlayer *)player
{
[self updateCurrentTimeForPlayer:player];
if (player.playing)
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
else
{
[playButton setImage:((player.playing == YES) ? pauseBtnBG : playBtnBG) forState:UIControlStateNormal];
}
}
-(void)updateViewForPlayerInfo:(AVAudioPlayer*)player
{
duration.text = [NSString stringWithFormat:@"%d:%02d",(int)player.duration / 60, (int)player.duration % 60, nil];
progressBar.maximumValue = player.duration;
}
- (void)updateCurrentTime
{
[self updateCurrentTimeForPlayer:soundPlayer];
}
- (void)updateCurrentTimeForPlayer:(AVAudioPlayer *)player
{
currentTime.text = [NSString stringWithFormat:@"%d:%02d", (int)soundPlayer.currentTime / 60, (int)soundPlayer.currentTime % 60, nil];
progressBar.value = player.currentTime;}
#pragma mark background notifications
- (void)registerForBackgroundNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(setInBackgroundFlag)
name:UIApplicationWillResignActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearInBackgroundFlag)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)setInBackgroundFlag
{
inBackground = true;
[self pausePlaybackForPlayer:soundPlayer];
}
- (void)clearInBackgroundFlag
{
inBackground = false;
//we are checking if palyer was playing, if so, we continue its playing.
if (paused == false) {
[self startPlaybackForPlayer:soundPlayer];
}
}
#pragma mark AVAudioPlayer delegate methods
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{if (flag == NO)
NSLog(@"Playback finished unsuccessfully");
[player setCurrentTime:0.0];
if (inBackground)
{
[self updateViewForPlayerStateInBackground:player];
}
else
{
[self updateViewForPlayerState:player];
}
}
// we will only get these notifications if playback was interrupted
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)p
{
NSLog(@"Interruption begin. Updating UI for new state");
// the object has already been paused, we just need to update UI
if (inBackground)
{
[self updateViewForPlayerStateInBackground:p];
}
else
{
[self updateViewForPlayerState:p];
}
}
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)p
{
NSLog(@"Interruption ended. Resuming playback");
[self startPlaybackForPlayer:p];
}
@end
我已经上传了示例应用程序
最佳答案
我建议您创建单例来管理您的 AVAudioPlayer,如下所示
界面
// AudioManager.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface AudioManager : NSObject
@property (nonatomic, strong) AVAudioPlayer *soundPlayer;
+ (id)sharedManager;
@end
// AudioManager.m
#import "AudioManager.h"
#define kSoundFileName @"sample.m4a"
实现
@implementation AudioManager
+ (id)sharedManager
{
static AudioManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (AVAudioPlayer *)soundPlayer
{
NSError *error;
if (_soundPlayer == nil)
{
_soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[self soundURL] error:&error];
}
return _soundPlayer;
}
- (NSURL *)soundURL
{
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"bgMusic" ofType:@"mp3"];
return [NSURL fileURLWithPath:soundFilePath];
}
@end
在其他 View Controller 中,您可以在单例对象上引用 AVAudioPlayer 属性,其中只会创建单个 AudioManager 对象,如下所示
#import "AudioManager.h"
[[[AudioManager sharedManager] soundPlayer] play];
关于ios - 播放声音后 childViewController 不释放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21062611/
我在 iOS/Swift 中遇到 childViewController 的多级层次结构问题。当前设置从最低到最高分为三层: InfoViewController SelectionViewContr
我已添加一个 View 作为 ChildViewcontroller,并且我只想在 ChildViewController 中关闭它并转到上一个 ViewController 来自 ParentVie
我有一个来自 Realm 的对象,我将每个控件分配给对象,我想要的是将每个 childVC 上分配的每个对象传递给下一个 childVC。 我在 UIPageViewcontroller 上初始化了该
我有一个看起来像这样的父/ subview Controller 系统 -- (Parent) RootViewController -- (Child) ChildController
最新更新:我创建了一个全新的项目,其构建方式与下面完全相同。然而,这个测试项目实际上工作得很好,即使它看起来是一样的......我注意到的一件事是损坏版本中的父 View Controller 正在接
我尝试实现左侧面板,但是当我实现时,它似乎除了容器之外不会显示任何内容。 下面是我的代码: override func viewDidLoad() { super.viewDidLoad()
我有一个继承自 BaseViewController.swift 的类。我已经为核心数据更改跟踪定义了一个标志: var coreDataUpdated: Bool? 我还在 BaseViewCont
我有一个 pageViewController (UIPageViewController),它有一个 subview Controller (名为 soundPlayer)。声音播放器有静态 AVA
我在 UICollectionViewController 上有一个 childViewController。我有所以我的 childViewController 出现在屏幕上。但是当我注册一个单元格
我在我的 iOS 应用程序中创建了一个汉堡包菜单。 我的 rootViewController 中有一个 navigationBar 但现在我需要将 navigationBar 移动到我的 child
好吧,希望我能解释得尽可能清楚。 我有一个 Parent ViewController,它有一个 ContainerView,它将包含不同的 viewControllers/Class。我正在像这样在
我有一个 View Controller ,其中有一个按钮和一个窗口。 按下按钮,窗口中的 View 应该改变。 窗口中的 View 是另一个 ViewController 及其 View 。 当按下
我正在使用 XLPagerTabStrip在 Swift 4 中创建一个基于类别的阅读应用程序。我了解到可以使用以下函数轻松创建静态数量的 ViewController。 override func
在实现与 subview Controller 连接的 View 容器时,我偶然发现了一个奇怪的问题。 层次结构如下: 我有一个 UISplitViewController,在 MasterViewC
我有一个 ViewController,它有添加按钮,该按钮将在屏幕右侧打开 SideBarVC。如果我再次点击主 ViewController 屏幕,它应该关闭我的 SideBarVC。 我试过了
我在我的 MainViewController 中放置了一个容器。然后在该容器内添加FirstViewController let containerView = UIView() view.addS
我有一个 ViewController,我可以像这样在其中添加许多 child : [self addChildViewController:myViewController]; 在此
这个问题在这里已经有了答案: Passing data between view controllers (45 个答案) 关闭 8 年前。 我有 2 个 View ,ParentViewContr
我正在尝试在 iPad 上从使用 UIPopoverController 切换到 ChildViewControllers。我们有 4 或 5 个 VC 在父导航 subview Controller
我有一个 UIPageViewController,它有三个 childViewController。这些 childViewControllers 正在显示图表,每 5 秒使用从网络连接获取的动态数
我是一名优秀的程序员,十分优秀!