How to properly implement multiple subview operations in IPAD - ios

I am creating an iPAD app. I have got a MAIN VIEW controller which covers the whole screen. And I have another class DROP-DOWN Class which i will be using to mimic the functionality of a drop-down as in Windows OS components. For that I am using a simple button, a scroll view and a few labels. I instantiate an object of that class and use it in my MAIN VIEW controller. To create the effect of a drop down, I alter the instance's views frame on the touch up of the button in toggle mode.(Click once, frame increased, click again frame decreased).
The creation part was fine, and the function to toggle to worked fine when implemented on a single instance of the DROP DOWN object.
But the problem comes when I instantiate these objects thru a for loop. The views seem to just vanish on button click.
Here is the code within the dropdowns.
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(10,54,100,50)];
scrollView.contentSize = CGSizeMake(50, 100);
for (int i = 0; i< 10; i++)
{
UILabel *newLable = [[UILabel alloc]initWithFrame:CGRectMake(0, 10*i, 100, 10)];
[newLable setFont:[UIFont boldSystemFontOfSize:10]];
newLable.text = [NSString stringWithFormat:#"Testing %i",i];
[scrollView addSubview:newLable];
}
scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrollView];
}
-(IBAction)btnClicked:(id)sender
{
if (!isOpen) {
oldRect = self.view.frame;
CGRect newFrame = self.view.frame;
newFrame.size = CGSizeMake(190, 200);
NSLog(#"New Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
self.view.frame = newFrame;
}
else {
self.view.frame = oldRect;
NSLog(#"Old Frame - %f,%f,%f,%f",self.view.frame.origin.x,self.view.frame.origin.y,self.view.frame.size.height,self.view.frame.size.width);
}
isOpen = !isOpen;
}
And here is how this is called from the main view.
- (void)viewDidLoad {
[super viewDidLoad];
[self performSelector:#selector(drawTheFilters)];
}
-(void)drawTheFilters
{
for (int i = 0; i<5 ; i++) {
DropDownView *dropView = [[DropDownView alloc]initWithNibName:#"DropDownView" bundle:[NSBundle mainBundle]];
CGRect newFrame = dropView.view.frame;
newFrame.origin = CGPointMake(200*i, 200);
dropView.view.frame = newFrame;
[self.view addSubview:dropView.view];
NSLog(#"Frame %i, %f,%f,%f,%f",i,dropView.view.frame.origin.x,dropView.view.frame.origin.y,dropView.view.frame.size.height,dropView.view.frame.size.width);
}
}

Related

ios - Delete or reset memory used when going back to root viewcontroller

Good day! Is there any way on how to reduce or revert the memory used to a freshly opened app state? I'm developing a Menu app for a restaurant. On the main screen i have a buttons for Main Course,Appetizers,Soups etc. If you click on one category it will take you to another view, That is UINavigation Controller with UITableViewController, now when you click on any row of that UITableView it will take you to another ViewController which contains UIScrollView with images. This Last ViewController has this code to loop many images.
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0,
self.view.frame.size.width,
self.view.frame.size.height)];
self.scrollView.pagingEnabled = YES;
[self.scrollView setAlwaysBounceHorizontal:NO];
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.scrollEnabled = YES;
//setup internal views
NSInteger numberOfViews = 5;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
image = [[UIImageView alloc] initWithFrame:
CGRectMake(xOrigin, 0,
self.view.frame.size.width,
self.view.frame.size.height)];
image.image = [UIImage imageNamed:[NSString stringWithFormat:
#"main_%d", i+1]];
image.contentMode = UIViewContentModeScaleAspectFit;
[self.scrollView addSubview:image];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(updatePrices:) userInfo:nil repeats:YES];
}
//set the scroll view content size
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width *
numberOfViews,
self.view.frame.size.height);
//add the scrollview to this view
[self.view addSubview:self.scrollView];
So basically in 1 Category Lets say Main Course, i have this
http://oi60.tinypic.com/f1fr0j.jpg
Each category i have that setup of views. Now, The main problem is, when i open the app, it only consumes 6MB, then when i Tap a button, to show the Menu for the selected category, it goes up to 17mb, Now When i click anything on the Food lists, and shows the image viewcontroller, it goes up to 68mb. I tried to add a button on each to and add some actions like this
- (IBAction)backToHome:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
self.view = nil;
}
The main problem is, When i go back to the MainViewController, The consumed Memory is there, so the app gets really slow if they select more categories because it increases the memory usage. How can i Reduce or "Clear Cache" of the used memory to prevent slowing the app? Thank you!
Calling imageNamed will always cache the image in the memory to increase speed, so event if you release holder view(s) the memory usage won't drop. Use some other method to load your images(imageWithContentsOfFile or other) and make caching by yourself if you need it at all.
#property(nonatomic, strong) NSMutableDictionary* cachedImages;
-(UIImage*)imageNamedBOOM:(NSString*)aImageName
{
UIImage* result = _cachedImages[aImageName];
if( !result )
{
result = [UIImage imageWithContentsOfFile:aImageName];
_cachedImages[aImageName] = result;
}
return result;
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
[_cachedImages removeAllObjects];
}
And then in your code just call it like this(note that you have to provide image extension png, jpg, etc):
-(void) setupScrollView {
//add the scrollview to the view
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0,
self.view.frame.size.width,
self.view.frame.size.height)];
self.scrollView.pagingEnabled = YES;
[self.scrollView setAlwaysBounceVertical:NO];
//setup internal views
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIImageView *image = [[UIImageView alloc] initWithFrame:
CGRectMake(xOrigin, 0,
self.view.frame.size.width,
self.view.frame.size.height)];
image.image = [self imageNamedBOOM:[NSString stringWithFormat:
#"image_%d.png", i+1]];//make sure here you provide proper image extension JPG, PNG or other
image.contentMode = UIViewContentModeScaleAspectFit;
[self.scrollView addSubview:image];
}
//set the scroll view content size
self.scrollView.contentSize = CGSizeMake(self.view.frame.size.width *
numberOfViews,
self.view.frame.size.height);
//add the scrollview to this view
[self.view addSubview:self.scrollView];
}
And in back to home:
- (IBAction)backToHome:(id)sender {
[_cachedImages removeAllObjects]
[self dismissViewControllerAnimated:YES completion:nil];
self.view = nil;
}

Issue with UIScrollView within UIScrollView

I have a UIScrollView in my storyboard that scrolls vertically. Inside of this scrollView, I have programmatically created a horizontal UIScrollView.
The vertical scrollView is called scroller. The horizontal UIScrollView is called scrollInfo. For some reason i am unable to register when the horizontal scrollInfo is being used. Any ideas on why this isn't working?
This is in my viewDidLoad:
scrollInfo = [[UIScrollView alloc] initWithFrame:CGRectMake(0,
155,self.view.frame.size.width, self.view.frame.size.height/4)];
//This will create a scrollview of device screen frame
scrollInfo.scrollEnabled = YES;
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0,
self.view.frame.size.width, self.view.frame.size.height/4)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5
alpha:1];
[scrollInfo addSubview:awesomeView];
}
// 3 views added horizontally to the UIScrollView by using xOrigin.
scrollInfo.contentSize = CGSizeMake(self.view.frame.size.width * numberOfViews,
self.view.frame.size.height/4);
[scroller addSubview:scrollInfo];
//Add scrollview to viewcontroller
scrollInfo.pagingEnabled = YES;
pageControl = [[UIPageControl alloc] init]; //SET a property of UIPageControl
pageControl.frame = CGRectMake(0,200,self.view.frame.size.width,
self.view.frame.size.height/8);
pageControl.numberOfPages = 3; //as we added 3 diff views
pageControl.currentPage = 0;
[scroller addSubview:pageControl];
Here is my scrollViewDidScroll method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if ([mainTableView isEqual: scrollView]) {
if (mainTableView.contentOffset.y > 0) {
// yourTableView is not on top.
NSLog(#"not top");
}
else {
// yourTableView is already on top.
NSLog(#"is top");
[UIView animateWithDuration:0.5f delay:0
options:UIViewAnimationOptionCurveLinear animations:^{
scroller.contentOffset = CGPointMake(0, 0);
} completion:NULL];
}
}
else {
CGFloat pageWidth = self.view.frame.size.width;
int page = floor((scrollInfo.contentOffset.x - pageWidth / 2 ) / pageWidth) +
1; //this provide you the page number
pageControl.currentPage = page;// this displays the white dot as current page
NSLog(#"page");
}
}
Looks like you forget to connect UIScrollViewDelegate to your scrollviews. After doing this you better operate with scrollview.tag to recognize which scrollview calling delegate

App crashes when UIScrollView touched

I have a RootViewController which creates a SecondViewController. Inside the SecondViewController in viewDidAppear, I create a UIScrollView and a UIPageControl and add them to the view. I also do this: scrollview.delegate = self. The SecondViewController is a <UIScrollViewDelegate>. I have also implemented scrollViewDidScroll inside the SecondViewController. All of this works, compiles and runs. However, when I touch the UIScrollView, the app crashes. For the life of me, I cannot figure out why. It is the stupidest problem, but I cannot solve it.
It is very similar to this problem: UIScrollView EXC_BAD_ACCESS crash in iOS
However, I tried those solutions and none of them work.
I have pasted some code below. I really appreciate the help.
RootViewController:
- (void)viewDidAppear:(BOOL)animated
{
SecondViewController *secondViewController = [[SecondViewController alloc] init];
secondViewController.view.frame = CGRectMake(50, 50, 925, 600);
secondViewController.view.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:secondViewController.view];
}
SecondViewController:
- (void)viewDidAppear:(BOOL)animated
{
CGRect scrollViewFrame = CGRectMake(50, 50, 824, 500);
self.scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
self.scrollView.delegate = self;
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width*3,
self.scrollView.frame.size.height);
self.scrollView.backgroundColor = [UIColor darkGrayColor];
self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
self.scrollView.pagingEnabled = YES;
[self.view addSubview:self.scrollView];
CGRect pageControlFrame = CGRectMake(0, 0, 50, 20);
self.pageControl = [[UIPageControl alloc] initWithFrame:pageControlFrame];
self.pageControl.numberOfPages = 3;
self.pageControl.currentPage = 0;
self.pageControl.backgroundColor = [UIColor grayColor];
[self.view addSubview:self.pageControl];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat pageWidth = self.scrollView.frame.size.width;
float fractionalPage = self.scrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
self.pageControl.currentPage = page;
}
your secondViewController object is deallocated as it is not retained anywhere only its view is retained as it is added as subview. You also need to retain secondViewController's object.

UIView animate alpha when view is offscreen doesn't work

I have a UIView which is located offscreen and I'm animating the frame so that the view slides in offscreen from the bottom and is visible. I'd like to simultaneously animate the alpha property of a UILabel on the view as well so it fades in. Unfortunately it appears I can't do the alpha animation because the view is offscreen and doesn't appear to take hold. It looks something like this:
nextCell.titleLabel.alpha = 0;
[UIView animateWithDuration:collapsedAnimationDuration animations:^{
CGRect newFrame = lastCell.frame;
newFrame.origin = CGPointMake(lastCell.frame.origin.x , lastCell.frame.origin.y + THREAD_CELL_HEIGHT);
nextCell.frame = newFrame;
nextCell.titleLabel.alpha = 1;
}];
Is it not possible to start animating the alpha of the subview because it's offscreen? If I position the view on screen and then try the animation it looks great but that's not the effect I'm going for. Thanks for your help.
Is this code executed in cellForRowAtIndexPath? If so, try moving it to tableView:willDisplayCell:forRowAtIndexPath:. The table view resets various properties of the cell before displaying it.
From the AppDelegate didFinishLaunching method:
self.myView = [[MyView alloc] initWithFrame:CGRectMake(320, 480, 400, 400)];
self.myView.titleLabel.text = #"test text";
self.myView.titleLabel.alpha = 0;
[UIView animateWithDuration:10.0 animations:^{
CGRect newFrame = self.myView.frame;
newFrame.origin = CGPointMake(0 , 0);
self.myView.frame = newFrame;
self.myView.titleLabel.alpha = 1;
}];
[self.viewController.view addSubview:self.myView];
MyView is just this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:self.titleLabel];
}
return self;
}
- (UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
}
return _titleLabel;
}
I did no important changes to the code you presented and it worked fine. So assuming you're not doing what Tim mentioned (it won't work if you're doing it), we need more details to help you out.

UIPageControl and UIScrollView during landscape orientation

I am having problems with UIPageControl and UIScrollView.
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGFloat pageWidth = self.scrollView.frame.size.width;
int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
self.pageControl.currentPage = page;
}
The problem occurs when I change the orientation to landscape. image1.jpg shares the view with image2.jpg, which is not what I want.
But if I change the orientation back to portrait, the view is doing fine.
The problem is that the page size has changed (i.e. the width of the scrollview) but the width of the images hasn't changed.
One solution would be that when you rotate you re-do the computation that sizes the UIImageViews and computes the scrollview's contentSize. That is, move the for loop and assignment to scrollView.contentSize out of viewDidLoad and into a private method, say sizeImagesAndSetContentSize. Call that from willAnimateRotationToInterfaceOrientation:duration:
Something like the following:
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
[self sizeImagesAndSetContentSize];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
- (void) sizeImagesAndSetContentSize {
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
[self sizeImagesAndSetContentSize];
}
By the way, viewDidLoad is not the place to do geometry calculations -- like things that involve the frame of the scrollView because the geometry isn't set then. in viewDidLoad the sizes of things are the sizes that they were in the storyboard (or nib) and haven't been adjusted to the actual device and the actual orientation. If it works for you so far, it is because you start out in the orientation that the storyboard is laid out. Probably the best place to do that, and thus to call sizeImagesAndSetContentSize is in the method viewDidLayoutSubviews. In fact, if you move the call there, you won't need to explicitly call it on rotation because when rotation occurs the view lays out the subviews and viewDidLayoutSubviews will get called automatically. So I'd suggest:
- (void)viewDidLoad
{
[super viewDidLoad];
imageArray = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", nil];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
pageControl.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
}
- (void) sizeImagesAndSetContentSize {
for (int i = 0; i < [imageArray count]; i++) {
CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [imageArray count], scrollView.frame.size.height);
}
- (void)viewDidLayoutSubviews {
[self sizeImagesAndSetContentSize];
}
Addendum:
ADDING TEXT TO THE IMAGE:
To add text on top of the image -- you could add a text element, like a label, "on top" of the scroll view. Consider the screenshot from an app below. In the top-level Cloud Slide Show View there is a full-sceen Scroll View and at the same hierarchy level but further down the list of subviews (and thus "on top" of the scrollview) are a label for a description, 3 buttons, and a UIPageControl. The label is set to resize on rotation via struts and springs (this runs on iOS 5) so you don't have to mess with it.
This application changes the description label to match the image that is currently displayed in the scrollview page -- so it has to detect page changes. This app does that in the scroll view delegate protocol method scrollViewDidEndDecelerating: where it decides which image is on-screen when scrolling stops and fills-in the correct description.
An alternative would be to put put a label on top of each UIImage, but that is messy which is why my app doesn't do that. You'd probably want a view to contain both the UIImage and a label so that repositioning the label would make sense. Probably create that in code... For me, too much trouble.
As an aside, I should say that using page mode in a scroll view is probably not the "best practice" way to do this anymore. I'm converting my app to use a UIPageViewController which should simplify the app. For instance, you don't have to do the resize-on-rotate thing since the individual child VCs of the UIPageViewControl can resize themselves automatically.

Resources