Memory Leak in using MPMoviePlayerController - ios

In my project I manually create views without using storyboard. I have tried to play a Video when I tap an image. It works fine. But everytime when I check it shows memory leak when I tap the image, I have searched about this a lot and applied but no use.
In my Appdelegate.h file:
#property (strong, nonatomic) MPMoviePlayerController *theMoviePlayer;
#property (strong, nonatomic) UIImageView *image1;
In .m file:
-(void) startPage{
.....
_image1 = [[UIImageView alloc] initWithFrame:CGRectMake((self.window.frame.size.width/2)-25, 40, 50, 50)];
[_image1 setUserInteractionEnabled:YES];
_image1.image = [UIImage imageNamed:#"image_2.jpg"];
_tapImage1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(image1Tapped:)];
[_image1 addGestureRecognizer:_tapImage1];
.....}
In imageTapped(),
-(void) image1Tapped:(UITapGestureRecognizer *)sender
{
.....
[_image1 removeFromSuperview];
_theMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
[_theMoviePlayer setControlStyle:MPMovieControlStyleFullscreen];
[_theMoviePlayer.view setFrame:CGRectMake(0,-55, self.window.frame.size.width, self.window.frame.size.height)];
[_theMoviePlayer setScalingMode:MPMovieScalingModeAspectFill];
UIWindow *backgroundWindow = [[UIApplication sharedApplication] keyWindow];
[backgroundWindow addSubview:_theMoviePlayer.view];
[_theMoviePlayer.view bringSubviewToFront:backgroundWindow];
[_theMoviePlayer play];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(movieFinished:)
name:MPMoviePlayerPlaybackDidFinishNotification object:_theMoviePlayer];
...}
I get a memory leak everytime it enters imageTapped: method. Any help will be appreciated.

#property (strong, nonatomic) MPMoviePlayerController *theMoviePlayer;
#property (weak, nonatomic) UIImageView *image1;
another think is that your theMoviePlayer is not remove, try to make its view transparency for see if is already working behind the new one

I am trying to help you.
In -(void) startPage you allocate _image1 object
And you are removing object in method -(void) image1Tapped:(UITapGestureRecognizer*)sender
using [_image1 removeFromSuperview]; method
means now _image1 is nil and when -(void) image1Tapped:(UITapGestureRecognizer *)sender method called at that time when u fetch _image1 object at that time _image1 is already nil
so its give Memory Leak warning.
And solution for this one is:
1.**Show/Hide _image1 object** or
every time you need to do proper allocation and remove image1 object in this method -(void) image1Tapped:(UITapGestureRecognizer *)sender, based your requirement.
First try this solution and there will be Memory Leak Warning removed.
The Compiler check all over steps in advance so it recognize as Warning.
In Some cases if your logic is wrong then compiler inform us Wrong logic.
if u want to check that click on blue arrow of memory warning button it will give exaplanation of your logic or warning with assumption.

I found the problem, its with the device iPad(iOS version 5). It shows no leaks when I checked with iPad4 (iOS version 7).

Related

Video is not plying on GMSMarker

I am working on play video on GMSMarker pin and below is my code. The display and hide/show pin view, everything work fine. Except video not play. I am using story board with pinview.
#interface MapViewController () <GMSMapViewDelegate>
#property (strong, nonatomic) IBOutlet GMSMapView *mapView;
#property (strong, nonatomic) IBOutlet UIView *pinView;
#property (strong, nonatomic) GMSMarker *london;
#property (strong, nonatomic) AVPlayer *player;
#property (strong, nonatomic) AVPlayerLayer *playerLayer;
#end
#implementation MapViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:51.5 longitude:-0.127 zoom:18];
self.mapView.camera = camera;
self.mapView.delegate = self;
CLLocationCoordinate2D position = CLLocationCoordinate2DMake(51.5, -0.127);
_london = [GMSMarker markerWithPosition:position];
_london.title = #"London";
_london.tracksViewChanges = YES;
_london.map = self.mapView;
[self setUpVideoPlayer];
}
- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker {
[self.player play];
return self.pinView;
}
-(void)setUpVideoPlayer{
NSString *videoFilePath = [[NSBundle mainBundle] pathForResource:#"SampleVideo" ofType:#"mp4"];
AVAsset *avAsset = [AVAsset assetWithURL:[NSURL fileURLWithPath:videoFilePath]];
AVPlayerItem *avPlayerItem =[[AVPlayerItem alloc]initWithAsset:avAsset];
self.player = [[AVPlayer alloc]initWithPlayerItem:avPlayerItem];
self.playerLayer =[AVPlayerLayer playerLayerWithPlayer:self.player];
[self.playerLayer setFrame:self.pinView.frame];
[self.pinView.layer addSublayer:self.playerLayer];
[self.player seekToTime:kCMTimeZero];
}
Please help me to fix the issue. Why the video is not playing.
Thanks in advance.
Normally, info window will not regenerate after showing it(its adding as an image). To show video frames it need to regenerate when video play. It means window need to refresh automatically. To do it you need to set one property for your GMSMarker. Line as follows,
_london.tracksInfoWindowChanges = YES;
So your full code is
CLLocationCoordinate2D position = CLLocationCoordinate2DMake(51.5, -0.127);
_london = [GMSMarker markerWithPosition:position];
_london.title = #"London";
_london.tracksViewChanges = YES;
_london.tracksInfoWindowChanges = YES;
_london.map = self.mapView;
Google documentation is https://developers.google.com/maps/documentation/ios-sdk/marker#set_an_info_window_to_refresh_automatically
I believe that the reason why your code doesn't work is the fact that Google Maps for iOS renders Custom Info Views as Images. I'm not sure, but I believe, since I have also tried with animations and it also doesn't work. When I use animation, I first see the initial state of animation and when I open marker info window again, I see the final state, without animation. Also, when I add a text field on custom info window view, I can't click on that text field. When I try with the video like you, I see a blank window like the player is going to start and hear the sound, but the video never starts.
I have found this note on many questions on Stackoverflow (they say that it is from Google Docs), but I don't see it on Google Docs:
Note: The info window is rendered as an image each time it is displayed on the map. This means that any changes to its properties, while it is active, will not be immediately visible. The contents of the info window will be refreshed the next time that it is displayed.
Source: Google Maps Showing speech bubble in marker info window iOS
Maybe you can try with tracksInfoWindowChanges = YES:
- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker {
marker.tracksInfoWindowChanges = YES;
self.player play];
return self.pinView;
}
Source: How to force refresh contents of the markerInfoWindow in Google Maps iOS SDK
I know that you have set it in viewDidLoad method, but maybe setting it in a
- (UIView *)mapView:(GMSMapView *)mapView markerInfoWindow:(GMSMarker *)marker
method maybe will work, but I don't believe. A lot of people say that setting markerInfoWindow to YES works, but I have tried, and it doesn't work.
I have tried to use iOS Maps and video plays without problems. I have never had a problem with using Apple Maps. So, I'm sure that Google Maps isn't a good solution for this problem. Maybe you could fix it by some hack, but I have lost several hours to find a solution, without results. Maybe it can be done, for example, to move a UIView above map when the user moves his finger on the map or zooms in/out, or some other solution, but I prefer system solutions above hacks. In this case, I don't see system solution, maybe I'd find it if I spent more time to research. If I were you, I wouldn't use Google Maps for this.
Edit: I've just seen that my solution works on simulator. On real device (iPhone 7+) it doesn't work, i.e. I see player controls are animating, I see sound, but I don't see the video. But even if it works on real device like it works on simulator, user can't play or pause video, because he only sees images which are updating regularly.

How to stop background music playing from a different class playing in Xcode?

Ive set a music track to play within the ViewController.h and .m files as shown below. I want to stop this when a new scene is loaded. (.h):
#interface ViewController : UIViewController<AVAudioPlayerDelegate>{
AVAudioPlayer *startingMusic;
}
#property (nonatomic, retain) AVAudioPlayer *startingMusic;
#end
and then .m
#synthesize startingMusic;
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *music = [[NSBundle mainBundle] pathForResource:musicTrack ofType:#"mp3"];
startingMusic=[[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:music] error:NULL];
startingMusic.delegate=self;
startingMusic.numberOfLoops=-1;
[startingMusic play];
}
I then try and stop this from playing when the new scene loads is a different class called PlayLevel.m in an init method using this code.
ViewController *test = [[ViewController alloc] init];
[test.startingMusic stop];
However the music just continues to play when the new seen loads. Any suggestions?
When you do ViewController *test = [[ViewController alloc] init]; you created the new ViewController instance, it's mean you now have two ViewController's objects in the memory.
What you really need to do is call stop on your current one.
If you want to stop music from another controller which has no connection to your ViewController you can try doing this via notifications.
For example:
in your ViewController.h file, after imports:
static NSString * const kStopMusicNotificationName = #"kStopMusicNotificationName";
in your ViewController viewDidLoad:
[NSNotificationCenter.defaultCenter addObserverForName:kStopMusicNotificationName object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
[self.startingMusic stop];
}];
and when you need to stop music:
[NSNotificationCenter.defaultCenter postNotificationName:kStopMusicNotificationName object:nil];
You're going about this the wrong way around, you should really be stopping the music within ViewController and not from another class. ViewController should be self contained where ever possible.
How are you loading the 2nd scene? I imagine you're using a Segue?
If you are using a segue you can override the function -prepareForSegue which is a UIViewController function. Within -prepareForSegue stop the music then.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Stop Music
}

iOS UIActivityViewController using UIActivityItemProvider "forgets" items

I have a larger app that should be able to share multiple images.
I implemented this using UIActivityViewController and UIActivityItemProvider to have asynchronous usage of the items (so that i only have to prepare one image at a time and not have to fill the memory with all of them at once to share them).
I was "inspired" by the Apple Airdrop example:
AirdropSample download
However when using my app to share e.g. 9 images (to camera Roll == "Save 9 images") only 4 to 7 images end up being in the camera roll, no error messages whatsoever.
If i repeat it over and over sometimes i get 5 images or 6 seemingly random.
I cannot post my app here, but i modified the above sample in a way that it will also randomly "fail" with delivering all images to the camera Roll...
If you download above sample and replace 4 files with these, it shows the problem:
APLAsyncImageViewController.h:
#import <UIKit/UIKit.h>
#import "APLAsyncImageActivityItemProvider.h"
#interface APLAsyncImageViewController : UIViewController
#end
APLAsyncImageViewController.m:
#import "APLAsyncImageViewController.h"
#import "APLProgressAlertViewController.h"
NSString * const kProgressAlertViewControllerIdentifier = #"APLProgressAlertViewController";
#interface APLAsyncImageViewController ()
#property (strong, nonatomic) UIWindow *alertWindow;
#property (strong, nonatomic) APLProgressAlertViewController *alertViewController;
#property (strong, nonatomic) UIPopoverController *activityPopover;
#property (weak, nonatomic) IBOutlet UIButton *shareImageButton;
- (IBAction)openActivitySheet:(id)sender;
#end
#implementation APLAsyncImageViewController
- (IBAction)openActivitySheet:(id)sender
{
NSMutableArray *itemArray = [[NSMutableArray alloc] init];
for( int i = 0; i < 9;i++)
{
APLAsyncImageActivityItemProvider *aiImageItemProvider = [[APLAsyncImageActivityItemProvider alloc] init];
[itemArray addObject: aiImageItemProvider];
}
//Create an activity view controller with the activity provider item. UIActivityItemProvider (AsyncImageActivityItemProvider's superclass) conforms to the UIActivityItemSource protocol
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:itemArray applicationActivities:nil];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
//iPhone, present activity view controller as is
[self presentViewController:activityViewController animated:YES completion:nil];
}
else
{
//iPad, present the view controller inside a popover
if (![self.activityPopover isPopoverVisible]) {
self.activityPopover = [[UIPopoverController alloc] initWithContentViewController:activityViewController];
[self.activityPopover presentPopoverFromRect:[self.shareImageButton frame] inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else
{
//Dismiss if the button is tapped while pop over is visible
[self.activityPopover dismissPopoverAnimated:YES];
}
}
}
#end
APLAsyncImageActivityItemProvider.h:
#import <UIKit/UIKit.h>
#interface APLAsyncImageActivityItemProvider : UIActivityItemProvider
#end
APLAsyncImageActivityItemProvider.m:
#import "APLAsyncImageActivityItemProvider.h"
#import "UIImage+Resize.h"
#implementation APLAsyncImageActivityItemProvider
- (id)activityViewControllerPlaceholderItem:(UIActivityViewController *)activityViewController
{
return [[UIImage alloc] init];
}
- (id)item
{
UIImage *image = [UIImage imageNamed:#"Flower.png"];
//CGSize imageSize;
//imageSize.height = 1000;
//imageSize.width = 1000;
//image = [UIImage imageWithImage:image scaledToFitToSize:imageSize];
return image;
}
- (UIImage *)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(NSString *)activityType suggestedSize:(CGSize)size
{
//The filtered image is the image to display on the other side.
return [[UIImage alloc] init];
}
#end
If you execute the sample like this (Use menu item "Send Image After Preprocessing", press "SHARE" Button) it will often or mostly fail to deliver all 9 images to the camera roll.
IF you uncomment the 4 lines in "APLAsyncImageActivityItemProvider.m" that basically just scale the output image THEN it will work ALWAYS.
Can you tell me why ? I feel that if i know the answer to that riddle i can also fix my app.
Thank you,
Nils
The reason for this issue is pretty simple.
There are limited number of writer threads available to use by the app, so writing too many images simulteneously may fail due to "Write Busy" error. You can verify that by trying to manually write these images using UIImageWriteToSavedPhotosAlbum (or corresponding Asset/Photos framework counterparts).
So, resizing images just hides the real problem because writing a smaller image takes less time.
The problem can be solved by liming the number of writers and/or retrying in case of error.
Also note that -[UIActivityItemProvider item] is a blocking call, but there are no synchronous ways to write to the gallery out of the box. This can be handled with dispatch_semaphore_wait , NSCondition, etc.
Or, in case you're using ReactiveCocoa, by simply calling waitUntilCompleted.
I would go to say that the [UIImage imageWithImage:image scaledToFitToSize:imageSize] image created is GC before saved to camera roll, but I don't know what is going on in that method without the code. That method is not a normal UIImage method, a category maybe?
EDIT
Replace your for loop code adding to your item array with this code, and then uncomment your resize lines, and see if that works.
NSMutableArray *itemArray = [[NSMutableArray alloc] init];
for( int i = 0; i < 9;i++)
{
UIImage *aiImage = [[[APLAsyncImageActivityItemProvider alloc] init] image];
[itemArray addObject:aiImage];
}

Passing a UIImage to a SubView's UIImageView?

I'm trying to pass a UIImage value to a UIImageView inside a View that I'm adding as a subView.
Here's what I'm doing in my mainViewController:
- (IBAction)selectStore:(id)sender {
StoreSelectorViewController *SSVC = [[StoreSelectorViewController alloc] initWithNibName:#"StoreSelectorViewController" bundle:nil];
[self.navigationController addChildViewController:SSVC];
[SSVC.backgroundView setImage:[UIImage imageNamed:#"CoolBG.png"]];
[self.view.window addSubview:SSVC.view];
}
I did the following in my StoreSelectorViewController.h file:
#property (nonatomic, retain) IBOutlet UIImageView *backgroundView;
And I also linked it in the XIB file.
Everything loads, except that I never receive the UIImage value. Thanks
Sometimes using this doesn't seem to work:
[SSVC.backgroundView setImage:[UIImage imageNamed:#"CoolBG.png"]];
So instead I use this two line version, in a slightly dif't order, setting the image, before displaying its parent (SSVC):
StoreSelectorViewController *SSVC = [[StoreSelectorViewController alloc] initWithNibName:#"StoreSelectorViewController" bundle:nil];
UIImage *img = [UIImage imageNamed:#"CoolBG.png"];
[SSVC.backgroundView setImage:img];
[self.navigationController addChildViewController:SSVC];
[self.view.window addSubview:SSVC.view];
You really shouldn't be calling addChildViewController regardless, it's not what you want. You're not making use of the UIViewController either, just stealing its view property. It's possible that because of this, the UIViewController is not fully instantiated since it's not presented (viewWill/Did) methods never get called, and so backgroundView is probably nil.
The problem is in this line [self.view.window addSubview:SSVC.view]; So just replace the line to below:-
Also you want to add imageview as a subview of view. So use below code where backgroundView is the imageview:-
[SSVC.view addSubview:SSVC.backgroundView];
[self.view addSubview:SSVC.view];.

showing a UITextField that is a local variable vs a property in a custom view

I am mocking up a quick demo of a project but am having a problem with a UITextField.
The behavior that we want is that when a user clicks on a button, there should be a custom view that appears with a UITextField and a UIButton in a custom view that overlays the main view.
I have a custom view called Searchview and the following in the Searchview.m. The problem is that when the textField is a property, it doesn't show but when it is a local variable, it does show. Can anybody help me with what is going on so that the UITextField shows? Is how I am doing this even the right idea (custom UIView or custom UIControl or a modal controller)? Finally, would setNeedsDisplay be appropriate here?
thx in advance
#interface Searchview()
#property (nonatomic, weak) UITextField *textField;
#end
- (void)drawRect:(CGRect)rect
{
// this doesn't work
self.textField = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 120.0f, 25.0f)];
self.textField.returnKeyType = UIReturnKeyDone;
self.textField.placeholder = #"Writer";
self.textField.borderStyle=UITextBorderStyleBezel;
[self.textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: self.textField];
/* this works
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0f, 10.0f, 120, 25)];
textField.returnKeyType = UIReturnKeyDone;
textField.placeholder = #"Writer";
textField.borderStyle=UITextBorderStyleBezel;
[textField addTarget:self
action:#selector(textFieldDone:)
forControlEvents:UIControlEventEditingDidEndOnExit];
[self addSubview: textField];
*/
UIButton *mButton=[UIButton buttonWithType:UIButtonTypeRoundedRect];
mButton.frame=CGRectMake(200.0f,10.0f,100.0f,37.0f);
[mButton setTitle:#"search" forState:UIControlStateNormal];
[mButton addTarget:self action:#selector(showSearchController:) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:mButton];
[self setNeedsDisplay];
}
As a property - not showing:
As a local variable - showing:
#property (nonatomic, strong) UITextField *textField;
change the weak to strong and change the self.textFiled to _textField to have a try
And make sure your textField property not be released
It's pretty simple when you think about it. ARC (approximately) converts the following code:
self.weakProp = [[Foo alloc] init];
to the equivalent of the following "manually reference-counted" code:
Foo * temp = [[Foo alloc] init];
self.weakProp = temp;
[temp release];
Nothing is retaining it, so it is released.
I can only think of two reasons to have assign/weak IBOutlets:
For an outlet in a VC, so it doesn't retain a subview when its view is set to nil (e.g. on a memory warning). This is less relevant in iOS 6.0 since views are not automatically released on a memory warning (so if you do it, you can release them all explicitly).
For a view where the outlet points to a superview (and would cause a retain cycle). This is quite rare.
In general, I prefer strong IBOutlets: They might keep objects alive for a little longer than necessary, but they are safer than assign and more efficient than weak. Just watch out for retain cycles!

Resources