AVPictureInPictureController - Custom PopOver Doesnt display - ios

I have used AVPictureInPictureController in AVPPlayer and initialized it as AVPlayerLayer.
- (id)initWithFrame:(CGRect)frame contentURL:(NSURL*)contentURL
{
self = [super initWithFrame:frame];
if (self) {
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:contentURL];
self.moviePlayer = [AVPlayer playerWithPlayerItem:playerItem];
playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.moviePlayer];
if ([AVPictureInPictureController isPictureInPictureSupported])
{
self.avVPictureInPictureController = [[AVPictureInPictureController alloc] initWithPlayerLayer:playerLayer];
}
[playerLayer setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
[self.moviePlayer seekToTime:kCMTimeZero];
[self.layer addSublayer:playerLayer];
self.contentURL = contentURL;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(playerFinishedPlaying) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
[self initializePlayer:frame];
}
return self;
}
Now when I try to present FPPopoverController ( a custom class to present PopOvers on iPhone and iPad ) it doesn't show on the screen.
I use the following code to present FPPopOverController
//That's how I call
self.audioSubtitlePopoverController.presentPopoverFromPoint(point)
//Definition of presentPopoverFromPoint
-(void)presentPopoverFromPoint:(CGPoint)fromPoint
{
self.origin = fromPoint;
//NO BORDER
if(self.border == NO)
{
_viewController.title = nil;
_viewController.view.clipsToBounds = YES;
}
_contentView.relativeOrigin = [_parentView convertPoint:fromPoint toView:_contentView];
[self.view removeFromSuperview];
NSArray *windows = [UIApplication sharedApplication].windows;
if(windows.count > 0)
{
_parentView=nil;
_window = [windows objectAtIndex:0];
//keep the first subview
if(_window.subviews.count > 0)
{
_parentView = [_window.subviews lastObject];
NSLog(#"%# %#",_parentView,_viewController);
[_parentView addSubview:self.view];
[_viewController viewDidAppear:YES];
[self.view bringSubviewToFront:_parentView];
}
}
else
{
[self dismissPopoverAnimated:NO];
}
[self setupView];
self.view.alpha = 0.0;
[UIView animateWithDuration:0.2 animations:^{
self.view.alpha = self.alpha;
}];
[[NSNotificationCenter defaultCenter] postNotificationName:#"FPNewPopoverPresented" object:self];
//navigation controller bar fix
if([_viewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *nc = (UINavigationController*)_viewController;
UINavigationBar *b = nc.navigationBar;
CGRect bar_frame = b.frame;
bar_frame.origin.y = 0;
b.frame = bar_frame;
}
}
Even present from view doesn't work too
self.audioSubtitlePopoverController.presentPopoverFromView(self.view)
// Definition of presentPopoverFromView
-(void)presentPopoverFromView:(UIView*)fromView
{
SAFE_ARC_RELEASE(_fromView);
_fromView = SAFE_ARC_RETAIN(fromView);
[self presentPopoverFromPoint:[self originFromView:_fromView]];
}
Can anyone please assist. Would be grateful.

FPPopover is deprecated.
You should use the native Possibility to display Popovers or WEPopover instead.

Related

Can't remove subview?

This is about a web browser.
I have a custom Class that handles the webpages.
SNBrowserView
SNBrowserViewPage
SNBrowserViewPages have two objects.
WKWebView
UIImageView // Snapshot of the WKWebView
A function either sustains or recovers a page for memory management.
(Testing) Whenever a page is selected I call a recovery function.
Selection:
- (void)browserView:(SNBrowserView *)browserView didSelectPage:(SNBrowserViewPage *)page
{
if (page.sustained) {
[page recoverAnimated:NO];
}
}
Sustain:
- (void)sustain
{
_sustained = YES;
if (_webView) {
_webView = nil;
[_webView removeFromSuperview];
}
_snapshotView = [[UIImageView alloc] init];
_snapshotView.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
_snapshotView.image = _snapshot;
[self addSubview:_snapshotView];
}
Recover:
- (void)recoverAnimated:(BOOL)animated
{
_sustained = NO;
_webView = [[WKWebView alloc] init];
_webView.frame = CGRectMake(0.0, 0.0, self.frame.size.width, self.frame.size.height);
_webView.navigationDelegate = self;
_webView.UIDelegate = self;
[self addSubview:_webView];
[self sendSubviewToBack:_webView];
[self loadURL:_initialURL]; // Start loading as early as possible.
if (animated) {
[UIView animateWithDuration:0.3
animations:^{
_snapshotView.alpha = 0.0;
}
completion:^(BOOL finished){
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}];
}
else {
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}
}
When I try to recover a page the snapshotView is not set to nil nor is it removed from the superview.
How is that even possible?
Even this won't work:
- (void)recoverAnimated:(BOOL)animated
{
_snapshotView = nil;
[_snapshotView removeFromSuperview];
}
The snapshotView is a subview, removeFromSuperview should always work, why is there MORE to it?
I suggest you try replace all of your
_view = nil;
[_view removeFromSuperview];
with
[_view removeFromSuperview];
_view = nil;
because what you are doing is setting the _view to nil and then removing nil from superview.

How to create custom slide menu without third party library.?

I want to implement slide menu in my iOS app like drawer (Andriod). I went through a tutorial, but all of them are using third party libraries. Is there any possibility to create a custom slide menu. I tried to create it with the following code, but it's only working with xib file:
- (IBAction)sidemenu:(id)sender
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureAction
{
UISwipeGestureRecognizer *swiperight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureRightAction:)];
swiperight.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
}
#pragma mark AddSwipeGestureLeftAndRight
- (void)SwipGestureRightAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
view.frame = self.view.frame;
} completion:^(BOOL finished) {
swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(SwipGestureLeftAction:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeLeft];
}];
}
- (void)SwipGestureLeftAction:(UISwipeGestureRecognizer *)swipeRight
{
[UIView animateWithDuration:0.50f animations:^{
[view setFrame:CGRectMake(self.view.frame.origin.x - self.view.frame.size.width, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
} completion:^(BOOL finished){
[self.view removeGestureRecognizer:swipeLeft];
}];
}
Here is what I have for you:
I made a super class for all my slide menus in all projects. It manages the showing and hiding of the slide menu, and handles orientation changes. It slides in from left on top of the current view, and it partially obscures the remainder of the view with a dark transparent background.
If you ever need other behaviour (like pushing out the current view) just override the animation part.
My slide menu is a Singleton because in our applications we only use one slide menu on every screen.
#import <UIKit/UIKit.h>
#interface IS_SlideMenu_View : UIView <UIGestureRecognizerDelegate>
{
UIView* transparentBgView;
BOOL hidden;
int lastOrientation;
}
#property (strong, nonatomic) UIView *menuContainerV;
+ (id)sharedInstance;
- (BOOL)isShown;
- (void)hideSlideMenu;
- (void)showSlideMenu;
#end
#import "IS_SlideMenu_View.h"
#implementation IS_SlideMenu_View
+ (id)sharedInstance
{
static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[[self class] alloc] init];
});
return _sharedInstance;
}
- (instancetype)initWithFrame:(CGRect)frame
{
frame = [[[UIApplication sharedApplication] delegate] window].frame;
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor clearColor];
transparentBgView = [[UIView alloc] initWithFrame:frame];
[transparentBgView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.6]];
[transparentBgView setAlpha:0];
transparentBgView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(gestureRecognized:)];
[transparentBgView addGestureRecognizer:tap];
[transparentBgView addGestureRecognizer:pan];
[self addSubview:transparentBgView];
frame.size.width = 280;
self.menuContainerV = [[UIView alloc] initWithFrame:frame];
CALayer *l = self.menuContainerV.layer;
l.shadowColor = [UIColor blackColor].CGColor;
l.shadowOffset = CGSizeMake(10, 0);
l.shadowOpacity = 1;
l.masksToBounds = NO;
l.shadowRadius = 10;
self.menuContainerV.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[self addSubview: self.menuContainerV];
hidden = YES;
}
//----- SETUP DEVICE ORIENTATION CHANGE NOTIFICATION -----
UIDevice *device = [UIDevice currentDevice];
[device beginGeneratingDeviceOrientationNotifications];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:device];
lastOrientation = [[UIDevice currentDevice] orientation];
return self;
}
//********** ORIENTATION CHANGED **********
- (void)orientationChanged:(NSNotification *)note
{
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight){
NSLog(#"%ld",orientation);
if(!hidden && lastOrientation != orientation){
[self hideSlideMenu];
hidden = YES;
lastOrientation = orientation;
}
}
}
- (void)showSlideMenu {
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
self.frame = CGRectMake(0, 0, window.frame.size.width, window.frame.size.height);
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-window.frame.size.width, 0)];
[window addSubview:self];
// [[UIApplication sharedApplication] setStatusBarHidden:YES];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformIdentity];
[transparentBgView setAlpha:1];
} completion:^(BOOL finished) {
NSLog(#"Show complete!");
hidden = NO;
}];
}
- (void)gestureRecognized:(UIGestureRecognizer *)recognizer
{
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[self hideSlideMenu];
} else if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
static CGFloat startX;
if (recognizer.state == UIGestureRecognizerStateBegan) {
startX = [recognizer locationInView:self.window].x;
} else
if (recognizer.state == UIGestureRecognizerStateChanged) {
CGFloat touchLocX = [recognizer locationInView:self.window].x;
if (touchLocX < startX) {
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(touchLocX - startX, 0)];
}
} else
if (recognizer.state == UIGestureRecognizerStateEnded) {
[self hideSlideMenu];
}
}
}
- (void)hideSlideMenu
{
UIWindow* window = [[[UIApplication sharedApplication] delegate] window];
window.backgroundColor = [UIColor clearColor];
[UIView animateWithDuration:0.5 animations:^{
[self.menuContainerV setTransform:CGAffineTransformMakeTranslation(-self.window.frame.size.width, 0)];
[transparentBgView setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
[self.menuContainerV setTransform:CGAffineTransformIdentity];
// [[UIApplication sharedApplication] setStatusBarHidden:NO];
hidden = YES;
NSLog(#"Hide complete!");
}];
}
- (BOOL)isShown
{
return !hidden;
}
#end
Subclasses only need to add subviews to the menuContainerV view, and manage them.
An example:
I created a subclass that has an header view and a table view as its content. I created the content view in a xib, and the owner of the xib is this subclass. This way I can bind outlets to the xib.
#import "IS_SlideMenu_View.h"
#interface CC_SlideMenu_View : IS_SlideMenu_View<UITableViewDelegate, UITableViewDataSource>
#property (weak, nonatomic) IBOutlet UIView *headerView;
#property (weak, nonatomic) IBOutlet UITableView *tableView;
...
#end
When the slide menu gets instantiated I load the xib and add the content view to the menuContainerV view.
#import "CC_SlideMenu_View.h"
#implementation CC_SlideMenu_View
- (instancetype)init
{
self = [super init];
if (self) {
UIView *v = [[[NSBundle mainBundle] loadNibNamed:#"CC_SlideMenu_View" owner:self options:nil] firstObject];
v.frame = self.menuContainerV.bounds;
[self.menuContainerV addSubview:v];
self.tableView.backgroundColor = [UIColor darkGrayColor];
}
return self;
}
...
#end
The result is something like this.

UIMenuController's canPerformAction method called on button tap

I have a message view in which user can see conversation and type & send message. Menu (kind of context menu) is displayed when user long taps on message. I have used TableView and Button for chat history and send message button respectively.
My problem is canPerformAction:withSender method is hit when I tap on Button or TableView. And moreover this code was working fine till iOS 9.0.
#interface MessageDetailViewController ()
- (void)updateDetailView;
- (void)setMessageBar;
- (void)setCallButton;
#end
#implementation MessageDetailViewController
{
}
#synthesize messageField;
#synthesize messageList;
#synthesize messageBarView;
#synthesize navigationBarView;
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"MessageDetailView: viewDidLoad method called.");
// Observe keyboard hide and show notifications to resize the text view appropriately.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didHideEditMenu:) name:UIMenuControllerDidHideMenuNotification object:nil];
[messageField becomeFirstResponder];
messageArray = [[NSMutableArray alloc] init];
typingImage = [[UIImageView alloc] init];
presenceImage = [[UIImageView alloc] init];
navigationBarView = [[UIView alloc] init];
viewHeight = self.view.frame.size.height;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[messageField setDelegate:self];
messageList.delegate = self;
messageList.dataSource = self;
[self updateDetailView];
[messageList reloadData];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
/**
* This method is called to send instance method to server.
*/
- (void)onSendMessageButtonPressed:(id)sender
{
if([messageField.text length] == 0)
{
return;
}
messageField.text = [messageField.text substringToIndex:160];
[[DatabaseManager getDatabaseManager] addMessageInDatabase:messageField.text];
[messageField setText:#""];
[self updateDetailView];
[messageList reloadData];
}
-(void)handleLongPress : (UILongPressGestureRecognizer *)gesture
{
TableViewCell *cell = nil;
UIMenuController *menu = [UIMenuController sharedMenuController];
if(gesture.state == UIGestureRecognizerStateBegan)
{
CGPoint p = [gesture locationInView:self.messageList];
NSIndexPath *ip = [self.messageList indexPathForRowAtPoint:p];
rowPosition = ip.row;
NSLog(#"row = %d", ip.row);
if(ip)
{
cell = (TableViewCell *)[messageList cellForRowAtIndexPath:ip];
[self becomeFirstResponder];
[menu setMenuItems:nil];
UIMenuItem *copyMenuItem = [[[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"COPY_MESSAGE", #"") action:#selector(copyMessage:)] autorelease];
UIMenuItem *deleteMenuItem = [[[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"DELETE_MESSAGE", #"") action:#selector(deleteMessage:)] autorelease];
if(cell.imFailIndication.image != nil)
{
UIMenuItem *resendMenuItem = [[[UIMenuItem alloc] initWithTitle:NSLocalizedString(#"RESEND_MESSAGE", #"") action:#selector(resendMessage:)] autorelease];
[menu setMenuItems:[NSArray arrayWithObjects:copyMenuItem, deleteMenuItem, resendMenuItem, nil]];
}
else
{
[menu setMenuItems:[NSArray arrayWithObjects:copyMenuItem, deleteMenuItem, nil]];
}
[menu setTargetRect:cell.frame inView:cell.superview];
[menu setMenuVisible:YES animated:YES];
return;
}
}
}
- (void)copyMessage:(id)sender
{
MessageDetailInfo *detailMessageInfo = [messageArray objectAtIndex:rowPosition];
[UIPasteboard generalPasteboard].string = detailMessageInfo.message;
}
- (void)deleteMessage:(id)sender
{
messageDeleteActionSheet = [[UIActionSheet alloc] initWithTitle:NSLocalizedString(#"PROMPT_MESSAGE_DELETE_CONFIRMATION", #"") delegate:self cancelButtonTitle:NSLocalizedString(#"CANCEL_BTN_TITLE", #"") destructiveButtonTitle:NSLocalizedString(#"DELETE_BTN_TITLE", #"") otherButtonTitles:nil, nil];
[messageDeleteActionSheet showFromTabBar:self.tabBarController.tabBar];
}
/*
The view implements this method to conditionally enable or disable commands of the editing menu.
The canPerformAction:withSender method is declared by UIResponder.
*/
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
UIMenuController *menu = (UIMenuController *)sender;
switch ([[menu menuItems] count])
{
case 0:
//This case is to display paste option for Message text field.
if(action == #selector(paste:))
{
return YES;
}
break;
case 2:
if(action == #selector(copyMessage:) || action == #selector(deleteMessage:))
{
return YES;
}
break;
case 3:
if(action == #selector(copyMessage:) || action == #selector(deleteMessage:) || action == #selector(resendMessage:))
{
return YES;
}
break;
default:
break;
}
return NO;
}
- (void)keyboardWillShow:(NSNotification *)notification
{
/*
Reduce the size of the text view so that it's not obscured by the keyboard.
Animate the resize so that it's in sync with the appearance of the keyboard.
*/
NSDictionary *userInfo = [notification userInfo];
// Get the origin of the keyboard when it's displayed.
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
// Get the top of the keyboard as the y coordinate of its origin in self's view's coordinate system. The bottom of the text view's frame should align with the top of the keyboard's final position.
CGRect keyboardRect = [aValue CGRectValue];
// Get the duration of the animation.
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
// Animate the resize of the text view's frame in sync with the keyboard's appearance.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
/* change position of messageBarView based on visibility of keypadview. */
CGRect messageBarFrame = messageBarView.frame;
messageBarFrame.origin.y = viewHeight - keyboardRect.size.height - messageBarView.frame.size.height;
messageBarView.frame = messageBarFrame;
if(messageBarFrame.origin.y == (viewHeight - keyboardRect.size.height - messageBarView.frame.size.height))
{
[messageField becomeFirstResponder];
}
/* change height of messageList based on visibility of keypadView. */
CGRect messageListFrame = messageList.frame;
messageListFrame.size.height = messageBarView.frame.origin.y;
messageList.frame = messageListFrame;
[UIView commitAnimations];
/* scroll to table view at last message if messages are available. */
if ([messageList numberOfRowsInSection:0])
{
[messageList scrollToRowAtIndexPath:[NSIndexPath indexPathForRow ([messageList numberOfRowsInSection:0] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
- (void)keyboardWillHide:(NSNotification *)notification
{
NSDictionary* userInfo = [notification userInfo];
/*
Restore the size of the text view (fill self's view).
Animate the resize so that it's in sync with the disappearance of the keyboard.
*/
NSValue *animationDurationValue = [userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey];
NSTimeInterval animationDuration;
[animationDurationValue getValue:&animationDuration];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:animationDuration];
/* change position of messageBarView based on visibility of keypadview. */
CGRect newFrame = messageBarView.frame;
newFrame.origin.y = self.view.frame.size.height - messageBarView.frame.size.height;
messageBarView.frame = newFrame;
/* Don't update tableview if datasource has been set nil. */
if(messageList.dataSource != nil)
{
/* change height of messageList based on visibility of keypadView. */
CGRect messageListFrame = messageList.frame;
messageListFrame.size.height = messageBarView.frame.origin.y;
messageList.frame = messageListFrame;
}
[UIView commitAnimations];
}
- (void)didHideEditMenu:(NSNotification *)notification
{
[[UIMenuController sharedMenuController] setMenuItems:nil];
}
- (void)didReceiveMemoryWarning
{
/* Releases the view if it doesn't have a superview. */
[super didReceiveMemoryWarning];
/* Release any cached data, images, etc that aren't in use. */
}
- (void)viewDidUnload
{
[super viewDidUnload];
/* Release any retained subviews of the main view.
e.g. self.myOutlet = nil; */
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
messageList.delegate = nil;
messageList.dataSource = nil;
}
-(BOOL)navigationShouldPopOnBackButton
{
NSLog(#"navigationShouldPopOnBackButton appearedFromComposeView = %d", [[NSNumber numberWithBool:appearedFromComposeView] intValue]);
if(appearedFromComposeView)
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
return YES; // Process 'Back' button click and Pop view controler
}
- (void)dealloc
{
[messageField release];
[messageList release];
[messageBarView release]
[messageArray release];
[super dealloc];
}
#end

ADInterstitialAd causing memory issues

I've been developing an iPhone / iPad game using Sprite Kit and in between each round I load an interstitial advert.
The interstitial is loaded on the main GameViewController and sits on top of the skview. I use a series of observers to trigger and cancel adverts and this all seems to work fine.
However, I've noticed some serious memory issues and after 4 or 5 rounds the app will crash. It appears to be directly related to the iAd interstitial. I've attached my code and you can see that I'm deallocating the objects, but the memory foot print does not seem to drop. I am using ARC too.
Does anyone know what could be causing this issue? I did read here: iAd & AdMob Heavy on Memory that the webkit view seems to hold on to its contents. I need to find a way to fix this, my code for my GameViewController is as follows:
#pragma mark - GAME LOAD
-(void)loadStartScreen{
_theView = (SKView *) self.view;
_theView.showsFPS = YES;
_theView.showsNodeCount = YES;
//Sprite Kit applies additional optimizations to improve rendering performance
_theView.ignoresSiblingOrder = YES;
// Create and configure the scene.
_theScene = [MainMenuScene sceneWithSize:_theView.bounds.size];
_theScene.scaleMode = SKSceneScaleModeAspectFill;
_theScene.backgroundColor = [UIColor grayColor];
// Present the scene
[_theView presentScene:_theScene];
// setup observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(requestFullScreenAd) name:#"requestAdvert" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(showFullScreenAd) name:#"showAdvert" object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cancelAdverts) name:#"cancelAdvert" object:nil];
}
#pragma mark - ADVERT CREATION AND SUPPORT
-(void)requestFullScreenAd {
// run the process on the main thread in a background queue
dispatch_async(bGQueue, ^{
if (_requestingAd == NO) {
_interstitial = [[ADInterstitialAd alloc]init];
_interstitial.delegate = self;
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicyManual;
NSLog(#"Ad Request");
_requestingAd = YES;
}
});
}
-(void)showFullScreenAd{
if (_adLoaded) {
CGRect interstitialFrame = self.view.bounds;
interstitialFrame.origin = CGPointMake(0, 0);
_adView = [[UIView alloc] initWithFrame:interstitialFrame];
[self.view addSubview:_adView];
[_interstitial presentInView:_adView];
_button = [UIButton buttonWithType:UIButtonTypeCustom];
[_button addTarget:self action:#selector(closeAd:) forControlEvents:UIControlEventTouchDown];
_button.backgroundColor = [UIColor clearColor];
[_button setBackgroundImage:[UIImage imageNamed:kCloseAd] forState:UIControlStateNormal];
_button.frame = CGRectMake(10, 10, 40, 40);
_button.alpha = 0.75;
[_adView insertSubview:_button aboveSubview:_adView];
[UIView beginAnimations:#"animateAdBannerOn" context:nil];
[UIView setAnimationDuration:1];
[_adView setAlpha:1];
[UIView commitAnimations];
}
}
-(void)closeAd:(id)sender {
[UIView beginAnimations:#"animateAdBannerOff" context:nil];
[UIView setAnimationDuration:1];
[_adView setAlpha:0];
[UIView commitAnimations];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
// notification for ad complete
[[NSNotificationCenter defaultCenter] postNotificationName:#"adClosed" object:nil];
}
-(void)cancelAdverts{
[_interstitial cancelAction];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
}
#pragma mark - IAD DELEGATE
-(void)interstitialAd:(ADInterstitialAd *)interstitialAd didFailWithError:(NSError *)error {
[_interstitial cancelAction];
_adView=nil;
_requestingAd = NO;
_button = nil;
_interstitial.delegate = nil;
_interstitial = nil;
NSLog(#"Ad didFailWithERROR");
NSLog(#"%#", error);
// request another advert if it failed
//[self requestFullScreenAd];
}
-(void)interstitialAdDidLoad:(ADInterstitialAd *)interstitialAd {
if (interstitialAd.loaded) {
_adLoaded = YES;
[[NSNotificationCenter defaultCenter]postNotificationName:#"adLoaded" object:nil];
}
NSLog(#"Ad DidLOAD");
}
-(void)interstitialAdDidUnload:(ADInterstitialAd *)interstitialAd {
[self closeAd:nil];
NSLog(#"Ad DidUNLOAD");
}
-(void)interstitialAdActionDidFinish:(ADInterstitialAd *)interstitialAd {
[self closeAd:nil];
NSLog(#"Ad DidFINISH");
}
Then in my level complete SKScene:
#pragma mark - SCENE APPEARS
-(void)didMoveToView:(SKView *)view {
// request an advert if advert removal is not purchased
if (![[[UserDetails sharedManager]iapAdsRemoved]boolValue]) {
// send request ad notification
[[NSNotificationCenter defaultCenter]postNotificationName:#"requestAdvert" object:nil];
// look for add loaded notification
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(adLoaded) name:#"adLoaded" object:nil];
// look for add completed
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(adShowCompleted) name:#"adClosed" object:nil];
}
// setup UI
[self createUI];
if (![[UnlockController sharedManager]allunlocksOpen]) {
// check all unlocks
[[UnlockController sharedManager]checkUnlocks:[[UserDetails sharedManager]userTotalScore]+[[UserDetails sharedManager]userLevelScore]];
// get the next unlock
[self getNextUnlockScore];
// set bar with correct increment
[unlockBar setBarValues:[[UserDetails sharedManager]userTotalScore]+[[UserDetails sharedManager]userLevelScore] increment:[[UserDetails sharedManager]userTotalScore] nextObject:nextScore];
}
else{
[self allUnlocksOpen];
}
// pre add button
preAdButtonPress = 3;
// variables
startCount = 0;
unlockItemsCount = 0;
allUnlocks = [[UnlockController sharedManager]finalUnlockOpen];
// start unlocks sequence
[self performSelector:#selector(runRewards) withObject:nil afterDelay:1.0];
}
-(void)willMoveFromView:(SKView *)view{
// cancel any adverts
[[NSNotificationCenter defaultCenter]postNotificationName:#"cancelAdvert" object:nil];
// remove observers
[[NSNotificationCenter defaultCenter]removeObserver:#"adClosed"];
[[NSNotificationCenter defaultCenter]removeObserver:#"adLoaded"];
}
Was a memory issue with some core code rather than the iad causing memory leaks.

iPhone development mpmovieplayer crashing

I am working on an app that will let me play different videos on the iPad remotely with an iPhone. I have been following along with apples example for a video player but I've been having some troubles. The videos play just fine and I can get it to play from a variety of videos but switching between them a few times it will crash and i get this in the debugger:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
*** First throw call stack:
(0x380da8bf 0x37c261e5 0x30acbcb5 0x30abc1f7 0x30ac3bf3 0x30c93d55 0x30c95f7b 0x380ad2dd 0x380304dd 0x380303a5 0x37e07fcd 0x31bb0743 0x25e5 0x257c)
This is the code I am using to create the player:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[self setMoviePlayerController:player];
[self installMovieNotificationObservers];
[player setContentURL:movieURL];
[player setMovieSourceType:sourceType];
[self applyUserSettingsToMoviePlayer];
[self.view addSubview:self.backgroundView];
[player.view setFrame:self.view.bounds];
[player.view setBackgroundColor = [UIColor blackColor];
[self.view addSubview:player.view];
}
And when the current movie is stopped I use:
[[self moviePlayerController] stop];
MPMoviePlayerController *player = [self moviePlayerController];
[player.view removeFromSuperview];
[self removeMovieNotificationHandlers];
[self setMoviePlayerController:nil];
Edit:
So Ive now discovered it happens every time i try and switch a video for the 11th time. weird! I'm practically pulling my hair out.
What fixed this problem for me was stopping the MPMoviePlayerController before doing the setContentURL.
MPMoviePlayerController *streamPlayer;
[streamPlayer stop];
[streamPlayer setContentURL:[NSURL URLWithString:selectedStation]];
In the implementation you have above, ARC doesn't know that the MPMoviePlayerController is finished and needs to be released.
Define MPMoviePlayerController in your .h file and make it accessible via a #property (and #synthesize).
#property (strong, nonatomic) MPMoviePlayerController * moviePlayerController;
Then take the result of your alloc & init and assign it to that. I.E.
self.moviePlayerController = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
you should just keep the moviePlayerController and if you want to play another video, just use
[self.moviePlayerController setContentURL:movieURL];
then in your notification callback:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
and please do not remove the notification handler from notification center, only do this in dealloc method of your VC.
now let's add some fade when the movie play is done:
- (void) moviePlayBackDidFinish:(NSNotification*)notification
{
[UIView animateWithDuration:1
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
// one second to fade out the view
self.moviePlayer.view.alpha = 0.0;
}
completion:^(BOOL finished){
self.moviePlayer = nil;
[self initanothermovieplayerandplay];
}
}
I had exactly the same problem.
Nothing was wrong with my and i guess with your code :)
Just a broken video file was mine problem.
Changing *.mov type to m4a for example fixed it. Maybe one or more of the files you play are corrupted?
Try to find out which files lead to crash and than if u can try to quickly forward backward the play position of one of them while playing - this should lead to crash in few tries.
This is how i found the bad files. By the way all my bad files were movies .mov made with Snapz Pro X :)
Not sure if it is the case here, but we had a lot of problems, because the MPMoviePlayer is a singleton somewhere under the hood.
What we did is, that we implemented our own MoviePlayer wrapper which can be used from UIView (actually we have exactly one subclass of UIView MoviePlayerView to show movies) and assures that only one instance of MPMoviePlayerController exists. The code goes like this (it contains some special stuff, we need to show previews/thumbs the way we want etc. you should clean up as well as some release-statements):
// MoviePlayer.h
#import <Foundation/Foundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "Logger.h"
#class MoviePlayerView;
#interface MoviePlayer : NSObject
{
#private
MPMoviePlayerController *controller;
MoviePlayerView *currentView;
}
#property (nonatomic, readonly) MPMoviePlayerController *controller;
+(MoviePlayer *) instance;
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view;
-(void) stopMovie;
#end
// MoviePlayer.m
#import "MoviePlayer.h"
#import "MoviePlayerView.h"
#implementation MoviePlayer
#synthesize controller;
static MoviePlayer *player = nil;
#pragma mark Singleton management
+(MoviePlayer *) instance
{
#synchronized([MoviePlayer class])
{
if (player == nil)
{
player = [[super allocWithZone:NULL] init];
player->controller = [[MPMoviePlayerController alloc] init];
player->controller.shouldAutoplay = NO;
player->controller.scalingMode = MPMovieScalingModeAspectFit;
player->currentView = nil;
}
return player;
}
}
+(id) allocWithZone:(NSZone *)zone
{
return [[self instance] retain];
}
-(id) copyWithZone:(NSZone *)zone
{
return self;
}
-(id) retain
{
return self;
}
-(NSUInteger) retainCount
{
return NSUIntegerMax;
}
-(oneway void) release
{
// singleton will never be released
}
-(id) autorelease
{
return self;
}
#pragma mark MoviePlayer implementations
-(void) stopMovie
{
#synchronized(self)
{
if (controller.view.superview)
{
[controller.view removeFromSuperview];
}
if (controller.playbackState != MPMoviePlaybackStateStopped)
{
[controller pause];
[controller stop];
}
if (currentView)
{
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc removeObserver:currentView name:MPMoviePlayerLoadStateDidChangeNotification object:controller];
[ntfc removeObserver:currentView name:MPMoviePlayerPlaybackStateDidChangeNotification object:controller];
currentView = nil;
}
}
}
-(void) playMovie:(NSURL*)movieURL onView:(MoviePlayerView *)view
{
#synchronized(self)
{
[self stopMovie];
currentView = view;
NSNotificationCenter *ntfc = [NSNotificationCenter defaultCenter];
[ntfc addObserver:currentView
selector:#selector(loadStateDidChange:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:controller];
[ntfc addObserver:currentView
selector:#selector(playbackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:controller];
[controller setContentURL:movieURL];
controller.view.frame = view.bounds;
[view addSubview: controller.view];
[controller play];
}
}
#end
// MoviePlayerView.h
#import <UIKit/UIKit.h>
#import "MoviePlayer.h"
#interface MoviePlayerView : MediaView
{
NSURL *movieURL;
NSURL *thumbnailURL;
UIImageView *previewImage;
UIView *iconView;
BOOL hasPreviewImage;
}
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie;
-(void) loadStateDidChange:(NSNotification *)ntf;
-(void) playbackStateDidChange:(NSNotification *)ntf;
#end
// MoviePlayerView.m
#import "MoviePlayerView.h"
#interface MoviePlayerView()
-(void) initView;
-(void) initController;
-(void) playMovie;
-(void) setActivityIcon;
-(void) setMovieIcon:(float)alpha;
-(void) clearIcon;
-(CGPoint) centerPoint;
#end
#implementation MoviePlayerView
-(id) initWithFrame:(CGRect)frame thumbnailURL:(NSURL *)thumbnail movieURL:(NSURL *)movie
{
self = [super initWithFrame:frame];
if (self)
{
movieURL = [movie retain];
thumbnailURL = [thumbnail retain];
[self initView];
[self initController];
hasPreviewImage = NO;
loadingFinished = YES;
}
return self;
}
-(void) dealloc
{
[iconView release];
[previewImage release];
[movieURL release];
[super dealloc];
}
-(void)initView
{
self.backgroundColor = [UIColor blackColor];
// add preview image view and icon view
previewImage = [[UIImageView alloc] initWithFrame:self.bounds];
[previewImage setContentMode:UIViewContentModeScaleAspectFit];
UIImage *img = nil;
if (thumbnailURL)
{
img = [ImageUtils loadImageFromURL:thumbnailURL];
if (img)
{
previewImage.image = img;
hasPreviewImage = YES;
}
}
[self addSubview:previewImage];
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
}
-(void)initController
{
UITapGestureRecognizer *rec = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(playMovie)];
[self addGestureRecognizer:rec];
[rec release];
}
-(void)playMovie
{
[[MoviePlayer instance] playMovie:movieURL onView:self];
[self setActivityIcon];
}
-(void) loadStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.loadState)
{
case MPMovieLoadStatePlayable:
{
[self clearIcon];
[controller setFullscreen:YES animated:YES];
break;
}
case MPMovieLoadStateStalled:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) playbackStateDidChange:(NSNotification *)ntf
{
MPMoviePlayerController *controller = [ntf object];
switch (controller.playbackState)
{
case MPMoviePlaybackStatePlaying:
{
[self clearIcon];
break;
}
case MPMoviePlaybackStateStopped:
{
[self setMovieIcon:(hasPreviewImage ? 0.8f : 0.3f)];
break;
}
case MPMoviePlaybackStatePaused:
{
[self setMovieIcon:0.8f];
break;
}
case MPMoviePlaybackStateInterrupted:
{
[self setActivityIcon];
break;
}
default:
{
break; // nothing to be done
}
}
}
-(void) setActivityIcon
{
[self clearIcon];
iconView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
iconView.center = [self centerPoint];
[self addSubview:iconView];
[iconView performSelector:#selector(startAnimating)];
}
-(void) setMovieIcon:(float)alpha
{
[self clearIcon];
iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"icon_movie.png"]];
iconView.center = [self centerPoint];
iconView.alpha = alpha;
[self addSubview:iconView];
}
-(void) clearIcon
{
if (iconView)
{
SEL stop = #selector(stopAnimating);
if ([iconView respondsToSelector:stop])
{
[iconView performSelector:stop];
}
[iconView removeFromSuperview];
[iconView release];
iconView = nil;
}
}
-(CGPoint) centerPoint
{
return CGPointMake(roundf(self.bounds.size.width / 2.0f), roundf(self.bounds.size.height / 2.0f));
}
-(void)resize
{
for (UIView *view in [self subviews])
{
if (view == iconView)
{
iconView.center = [self centerPoint];
continue;
}
view.frame = self.bounds;
}
[self addCaptionLabel];
}
-(void) layoutSubviews
{
[super layoutSubviews];
[self resize];
}
#end
...
player = [[MPMoviePlayerController alloc] initWithContentURL: [NSURL URLWithString:...
...
but I didn't gave internet connection to phone (wi-fi) :)
I had the same problem. My solution is using prepareToPlay:
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentOfURL:movieURL];
if (player) {
[player prepareToPlay];
//...
}
This error seems to be thrown for lots of different reasons, but the reason I found was that the MPMoviePlayerController class freaks out if you call methods in a certain order. From an IRC Channel:
"apparently if you call prepareToPlay WHILE setting source type and
NOT setting the view yet causes this crash"
So I fixed this by just making sure that I called prepareToPlay: LAST (or second to last, with the last being play:).
It is also weird because my original code worked in iOS 5.1, but this problem suddenly manifested when I started using the iOS 6.0 sdk. It is possibly a bug in the MPMoviePlayerController code, so I'm going to be filing a radar report on it, as calling prepareToPlay: before setting the view / setting the sourceFileType should not throw an exception (or at least an exception that seemingly has nothing to do with the actual error)

Resources