I am using the following code to show the splash screen :
UIImageView *defaultImage = [[UIImageView alloc] init];
defaultImage.frame = defaultImageFrame;
// for iOS 9 Compatability
//NSMutableArray *buttonsArray = [[NSMutableArray alloc] initWithCapacity:1];
NSMutableArray *buttonsArray = [NSMutableArray arrayWithCapacity:1];
for (int i = 1; i<=3; i++)
{
[buttonsArray addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"img00%d", i] ofType:#"png"]]];
}
mAnimatedButtons = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, WIDTH_DEVICE, HEIGHT_DEVICE)];
[mAnimatedButtons setUserInteractionEnabled:NO];
[mAnimatedButtons setAnimationImages:buttonsArray];
[mAnimatedButtons setAnimationRepeatCount:0];
[mAnimatedButtons setAnimationDuration:2.0];
[mAnimatedButtons startAnimating];
[defaultImage addSubview:mAnimatedButtons];
[self.view addSubview:defaultImage];
And i will remove the splash screen after this
[mAnimatedButtons stopAnimating]; //here animation stops
[mAnimatedButtons removeFromSuperview]; // here view removes from view hierarchy
mAnimatedButtons = nil;
self.defaultImage=nil;
[self.defaultImage removeFromSuperview];
[self.view.layer removeAllAnimations];
for (CALayer* layer in [self.view.layer sublayers])
{
[layer removeAllAnimations];
}
Adding this code increases the memory usage upto 300 Mb.
Removing this it has only 100 Mb.
I tried the following code also
self.defaultImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, WIDTH_DEVICE, HEIGHT_DEVICE)];
self.defaultImage.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"img001.png"],
[UIImage imageNamed:#"img002.png"],
[UIImage imageNamed:#"img003.png"],
nil];
//self.defaultImage.animationDuration = 1.0f;
self.defaultImage.animationRepeatCount = 0;
[self.defaultImage setAnimationDuration:2.0];
[ self.defaultImage startAnimating];
[self.view addSubview: self.defaultImage];
Even though the result is same.
Images, when used in the app, may require considerably more memory than the size of the asset in persistent storage might otherwise suggest. Assets are frequently compressed (e.g. JPG or PNG), but when you use the image, they're uncompressed, often requiring 4 bytes per pixel (one byte for red, green, blue, and alpha, respectively). So, for example, a iPhone 7+ full-screen retina image can require 14mb when you use the image. So, the memory-efficient technique is to employ lazy loading, not creating the UIImage objects until you absolutely need them. And, as Jerry suggested, because the amount of memory is determined by the size of the image and not the imageview in which you use the image, if your images have dimensions greater than required by the UIImageView in which you use them (i.e. width and height of the imageview times the "scale" of the device), you may want to resize the image accordingly.
Related
I am trying to make a simples application where the user can go through notes and then do a quiz. I am presenting this notes as images and the image would be assigned to an UIImageView to be shown to the user.
In my view did load I am mostly setting the UI. Then I would be creating my array containing my array of images (notes)
- (void)viewDidLoad {
UIColor *colourForNavigationBar = [self colorWithHexString:#"919D9F"];
[[UINavigationBar appearance] setBarTintColor:colourForNavigationBar];
UIColor *colourForBackground = [self colorWithHexString:#"F0F3F4"];
self.view.backgroundColor = colourForBackground;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont boldSystemFontOfSize:27.0];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
self.navigationItem.titleView = label;
label.text = #"Notes";
[label sizeToFit];
UIColor *colourForButton = [self colorWithHexString:#"919D9F"];
self.previousButton.backgroundColor = colourForButton;
self.nextButton.backgroundColor = colourForButton;
finishedNotes = NO;
playerPlaying = NO;
arrayOfImages = [NSArray arrayWithObjects:
[UIImage imageNamed:#"1.png"],
[UIImage imageNamed:#"2.png"],
[UIImage imageNamed:#"3.png"],
[UIImage imageNamed:#"4.png"],
[UIImage imageNamed:#"5.png"],
[UIImage imageNamed:#"6.png"],
[UIImage imageNamed:#"7.png"],
[UIImage imageNamed:#"8.png"],
[UIImage imageNamed:#"9.png"],
[UIImage imageNamed:#"10.png"],
[UIImage imageNamed:#"11.png"],
[UIImage imageNamed:#"12.png"],
[UIImage imageNamed:#"13.png"],
nil];
}
Then there is the 'next' button which allows the user to continue on to the next note. Below is the code for the action
- (IBAction)nextNote:(id)sender {
if (finishedNotes == YES) {
[self performSegueWithIdentifier:#"quizFromNotes" sender:self];
}
self.previousButton.enabled = YES;
stateOfPhoto++;
if (stateOfPhoto < 13) {
UIImage *image = [arrayOfImages objectAtIndex:stateOfPhoto-1];
self.imageView.image = image;
}
if (stateOfPhoto == 13) {
UIImage *image = [arrayOfImages objectAtIndex:stateOfPhoto-1];
self.imageView.image = image;
[self.nextButton setTitle:#"Begin Quiz" forState:UIControlStateNormal];
finishedNotes =YES;
}
if (playerPlaying == YES) {
[self.notesPlayer stop];
playerPlaying = NO;
}
}
Everything is working fine and smoothly. However whenever there is the change of image, the memory used by the application increasing by a few megabytes. Below is the photo of the memory usage.
From the black line onwards, when I press the 'next' button, the memory used increases. It just repeats itself until the user has continued onto the quiz. If I end the quiz and start over the notes, the memory usage would continue to rise from the previous memory usage (73 mb). Eventually, the app would crash and I cannot launch the app anymore.
I have never faced memory leakage issues before and hence, I am not sure of what to do. I have tried googling for the past hour and am not able to come up with a solution to this. So my question is how would I be able to release the memory or reduce the memory usage.
I think it's because of your images, how big are those images you're showing?
Based on this answer: https://stackoverflow.com/a/22662754/3231194, you can either use this method to scale your images before putting them into your UIImageView:
-(UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
But that will take up a CPU usage (I'm not sure though if it is a big deal).
OR
You can just resize your images by yourself before importing them to your Xcode project.
Just a general question about the best practices for functions which return values. Say for example I have the following function (pseudo code):
- (UIImageView *)createImageViewAndAddWithImageName:(NSString *)sName
{
UIImageView *iv = nil;
if (sName)
{
UIImage *image = [UIImage imageNamed:sName];
if (image)
{
iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[iv setImage:image];
[iv setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:iv];
}
}
return iv;
}
Now in some instances I want to perform further modifications like below:
UIImageView *iv = [self createImageViewAndAddWithImageName:#"Blah"];
[iv setAlpha:0.5f];
Where-as other times I want to just add an image:
[self createImageViewAndAddWithImageName:#"Blah"];
In the second instance, I presume that the memory will just be autoreleased? In the second instance, if this was running in a loop would it make sense to use an auto release block like below:
for (int i = 0; i < 100; i++)
{
#autoreleasepool {
[self createImageViewAndAddWithImageName:#"Blah"];
}
}
Just to free up the memory sooner? And is there any memory impact in having an autorelease pool in the main function like below too?
- (UIImageView *)createImageViewAndAddWithImageName:(NSString *)sName
{
UIImageView *iv = nil;
#autoreleasepool {
if (sName)
{
UIImage *image = [UIImage imageNamed:sName];
if (image)
{
iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[iv setImage:image];
[iv setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:iv];
}
}
}
return iv;
}
I just want to make sure that not using a returned value doesn't have a negative impact on my application, and that over-using autorelease pools doesn't either.
Thanks for any advice.
Every time the run loop calls out, it creates an autorelease pool. So all you have to do is to not allocate too much memory while doing anything caused by the run loop. For example if a user presses a button, your code handling this shouldn't allocate 100 MB of memory, even if autoreleased. Otherwise, autoreleased is reasonably cheap.
My main app has to do some work in a webview, so I want to keep the launch image up longer while this work takes place. To do this, I created a controller with a UIImageView and I'm loading the Default image up in it:
self.imageView.image = [UIImage imageNamed:"Default"];
// These all tend to fill the screen, but end up distorting the image
//self.imageView.contentMode = UIViewContentModeScaleAspectFill;
//self.imageView.contentMode = UIViewContentModeScaleAspectFit;
//self.imageView.contentMode = UIViewContentModeScaleToFill;
// This keeps the aspect the same, but doesn't fill the screen
self.imageView.contentMode = UIViewContentModeCenter;
This mostly works, except when the real launch image goes away and is replaced with mine, I have two white bars - one at the top and one at the bottom - where the image doesn't completely fill. I've tried to set the contentMode to the various fill/fit's, and even though this has the desired effect of filling the full screen, it stretches and distorts the image slightly.
So what I'm wondering is - what does the launch image do that I'm not doing? How can I replicate the display exactly so that the user can't tell it's a different image?
Like in your project I use a launch image and then in the first UIViewController called I have an UIImageView inside. The image resolution, must be exactly the same as the screen's definition.
The code in the UIViewController is something like this :
CGRect fullFrame = [[UIScreen mainScreen] bounds];
// Img Background
UIImageView *background;
if (fullFrame.size.height <= 480) {
background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image-480h.png"]];
}
else if (fullFrame.size.height <= 568) {
background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image-568h.png"]];
}
else if (fullFrame.size.height <= 668) {
background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image-668h.png"]];
}
else {
background = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image-736h.png"]];
}
background = CGRectMake(0, 0, frame.size.width, fullFrame.size.height);
myMainView = [[UIView alloc] initWithFrame:frame];
[myMainView addSubview:background];
self.view = myMainView;
I check the different screen size depending on the iPhone 4, 5, 6 and 6 plus.
I'm using a custom created loading spinner for my iOS app, but the edges looks rough on a coloured background as expected from a GIF.
How do other apps handle this situation? Do they use anything other than GIF, maybe APNG or something? Cause with GIF, the edges are always going to be rough, if matte is used, it would look good on just one color and not the others.
Any solution?
The UIImageView class provides the easiest way for developers to implement animations. All you need to do is to create an UIImageView object with a series of images for animating.
Example
// Load images
NSArray *imageNames = #[#"win_1.png", #"win_2.png", #"win_3.png"];
NSMutableArray *images = [[NSMutableArray alloc] init];
for (int i = 0; i < imageNames.count; i++) {
[images addObject:[UIImage imageNamed:[imageNames objectAtIndex:i]]];
}
// Normal Animation
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:CGRectMake(60, 95, 86, 193)];
animationImageView.animationImages = images;
animationImageView.animationDuration = 0.5;
[self.view addSubview:animationImageView];
[animationImageView startAnimating];
I make a app which include a image view within scroll view.
I have 3 classes and each class add images into image view.
When i press button1, it call class1 and it fill Class1images(from image001 to image200).
When i press button2, it call class2 and it fill Class2images(from image201 to image400)
The problem is when i call class1, image view show class1images(from image001 to image200).
After calling class1, i call class2.
When i call class2, my image view can't show class2images(from image201 to image400)
But, class1Images (from image001 to image200) are remaining in app.
I think class1 images are still in memory, so that class2 images can't add to memory.
I want to remove class1images when i call class2.
When i call next class, it will remove old images in memory and replace with new images.
But, my app is develop with storyboard and ARC.
So, i can't release memory manually in ARC mode.
Is there any ways to remove old images in memory?
- (void)viewDidLoad{
[super loadView];
self.view.backgroundColor = [UIColor grayColor];
ScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width,self.view.frame.size.height)];
ScrollView.pagingEnabled = YES;
NSInteger numberOfViews = 200;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
// Create a UIImage to hold Info.png
UIImage *image1 = [UIImage imageNamed:#"Image001.jpg"];
UIImage *image2 = [UIImage imageNamed:#"Image002.jpg"];
:
:
UIImage *image200 = [UIImage imageNamed:#"Image200.jpg"];
NSArray *images = [[NSArray alloc] initWithObjects:image1,image2, . . . ,image200,nil];
ImageView = [[UIImageView alloc] initWithFrame:CGRectMake(xOrigin, 0,self.view.frame.size.width, self.view.frame.size.height)];
[ImageView setImage:[images objectAtIndex:i]];
[ScrollView addSubview:ImageView];}
ScrollView.contentSize = CGSizeMake(self.view.frame.size.width*numberOfViews,self.view.frame.size.height);
[self.view addSubview:ScrollView];}
setting an imageview's animationImages property to a new array will remove the olf array and animate the new one.
to be 110% safe call stopAnimation, set the images, call startAnimation
I think your scrollview is not getting cleaned up. You need to remove previous images from scrollView and after that add images from next class.
try to implement something like this for removing images from scrollview and after this add other images on button click
for(UIView *subView in self.ScrollView.subviews)
{
if([subView isKindOfClass:[UIImageView class]])
[subView removeFromSuperview];
}
Hopefully can help you. Just give it a try.