Optimizing the page
I am developing for iPhone, SDK 3.1. I have about 150 images that I need to show to the user in order for them to view the pages. I copied the code from Apple's PageControl sample, but once I load it on the iPhone, the app crashes if I scroll quickly. I tried to write some optimizations to save memory, but it doesn't help much. I was wondering if someone could tell me if my optimization is in need or if I have another problem. The relevant code is below.
// ReviewViewController.h
@interface ReviewViewController : UIViewController <UIActionSheetDelegate, UIScrollViewDelegate> {
NSMutableArray *reviewArr;
NSMutableArray *viewControllers;
IBOutlet UIScrollView *scroller;
BOOL dirty;
NSInteger pageCount;
}
@property (retain, nonatomic) NSMutableArray *reviewArr;
@property (retain, nonatomic) NSMutableArray *viewControllers;
@property (assign, nonatomic) UIScrollView *scroller;
//ReviewViewController.m
-(void)clearScroller {
NSArray *subviews = [[NSArray alloc] initWithArray:scroller.subviews];
for (UIView *subview in subviews) {
//NSLog(@"DEBUG - view %d", subview.tag);
[subview removeFromSuperview];
}
[subviews release];
[scroller setContentOffset:CGPointMake(0,0) animated:NO];
}
- (void)initialize:(int)page {
if([reviewArr count] == 0) {
NSLog(@"No more cards on stack");
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 70, 420, 45)];
label.text = @"You have no review cards in your stack";
label.numberOfLines = 2;
label.font = [UIFont systemFontOfSize:20];
[scroller addSubview:label];
[label release];
[back setHidden:YES];
[forward setHidden:YES];
[mathFactAction setHidden:YES];
return;
}
NSLog(@"Initializing..%d", page);
[self setHeading];
[self clearScroller];
[mathFactAction setHidden:NO];
NSMutableArray *controllers = [[NSMutableArray alloc] init];
for (unsigned i = 0; i < reviewArr.count; i++) {
[controllers addObject:[NSNull null]];
}
self.viewControllers = controllers;
[controllers release];
// a page is the width of the scroll view
scroller.pagingEnabled = YES;
scroller.contentSize = CGSizeMake(scroller.frame.size.width * reviewArr.count, scroller.frame.size.height);
scroller.showsHorizontalScrollIndicator = NO;
scroller.showsVerticalScrollIndicator = NO;
scroller.scrollsToTop = NO;
scroller.delegate = self;
// pages are created on demand
// load the visible page
// load the page on either side to avoid flashes when the user starts scrolling
if(page > 0) {
[self loadScrollViewWithPage:page - 1];
}
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
}
- (void)checkForDirtyPages {
for (unsigned i = 0; i < [self.viewControllers count]; i++) {
if(i == pageCount-1) continue;
else if(i == pageCount) continue;
else if(i == pageCount+1) continue;
else unloadPage:i;
}
}
- (void)unloadPages {
dirty = FALSE;
[self unloadPage:pageCount-3];
[self unloadPage:pageCount-4];
[self unloadPage:pageCount+3];
[self unloadPage:pageCount+4];
}
- (void)unloadPage:(int)page {
if (page < 0) return;
if (page >= reviewArr.count) return;
// replace the placeholder if necessary
FactViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller != [NSNull null]) {
/*NSArray *subviews = [[NSArray alloc] initWithArray:controller.view.subviews];
for (UIView *subview in subviews) {
//NSLog(@"DEBUG - view %d", subview.tag);
[subview removeFromSuperview];
}
[subviews release]; */
// remove teh innerscroller from viewControllers to conserve memory
[self.viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];
}
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= reviewArr.count) return;
// replace the placeholder if necessary
FactViewController *controller = [viewControllers objectAtIndex:page];
//UIScrollView *innerScroller = [self.viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[FactViewController alloc] initWithReviewNumber:[reviewArr objectAtIndex:page]];
[self.viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
if (nil == controller.view.superview) {
CGRect frame = scroller.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
controller.view.frame = frame;
[scroller addSubview:controller.view];
//[self unloadPages];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender {
if(dirty) [self unloadPages];
//[self resetScroller:pageCount-1];
//[self resetScroller:pageCount+1];
}
- (void)scrollViewDidScroll:(UIScrollView *)sender {
// We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
// which a scroll event generated from the user hitting the page control triggers updates from
// the delegate method. We use a boolean to disable the delegate logic when the page control is used.
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = scroller.frame.size.width;
int page = floor((scroller.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
// pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// if we are on a new page
if(page != pageCount) {
dirty = TRUE;
pageCount = page;
[self setHeading];
}
// A possible optimization would be to unload the views+controllers which are no longer visible
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that not essential, such as cached data
[self checkForDirtyPages];
}
// FactViewController.m
@interface FactViewController : UIViewController {
IBOutlet UIScrollView *scrollView;
//IBOutlet UILabel *pageNumberLabel;
NSInteger review_id;
}
@property (nonatomic, retain) UIView *scrollView;
//@property (nonatomic, retain) UILabel *pageNumberLabel;
@property (assign, nonatomic) NSInteger review_id;
-(void)unloadImage;
- (id)initWithReviewNumber:(NSNumber *)reviewNumber;
- (int)getMathFactId;
@end
// Load the view nib and initialize the pageNumber ivar.
- (id)initWithReviewNumber:(NSNumber *)reviewNumber {
if (self = [super initWithNibName:@"FactViewController" bundle:nil]) {
self.review_id = [reviewNumber intValue];
}
return self;
}
- (void)dealloc {
//[pageNumberLabel release];
//[card release];
[scrollView release];
[super dealloc];
}
// Set the label and background color when the view has finished loading.
- (void)viewDidLoad {
NSLog(@"(FactVC) view did load");
[self showImage];
[super viewDidLoad];
}
-(void)unloadImage {
[scrollView release];
}
-(void)showImage {
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setMinimumIntegerDigits:2];
NSLog(@"Loading image (%d)", self.review_id);
NSString *img_file = [NSString stringWithFormat:@"%@", [numberFormatter stringForObjectValue:[NSNumber numberWithInt:self.review_id]]];
[numberFormatter release];
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:img_file ofType:@"gif"];
NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage *image = [UIImage imageWithData:imageData];
//UIImage *image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:img_file ofType:@"gif"]];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; // inDirectory:@"Resources/questions"
[scrollView addSubview:imageView];
[scrollView setContentSize:CGSizeMake(imageView.frame.size.width, imageView.frame.size.height)];
[imageView release];
}
Too bad it took so long, but I wanted to make sure it was all there.
+2
Ken
source
to share
No one has answered this question yet
See similar questions:
or similar: