6
\$\begingroup\$

I have a dining menu app that scrapes the data from a website and redisplays it in a mobile format, displayed below:

enter image description here

If the user swipes left and right, the app will show the previous/next meal (ex. if current meal is lunch, swiping left will show dinner).

I'm trying to come up with a more efficient way of switching between menus. With my current implementation, each menu is a separate view controller.

When someone swipes right, it calls:

-(void)swipeleft:(UISwipeGestureRecognizer*)gestureRecognizer {
 //swipes to next meal
 MealType curMeal = currentMenu.type;
 int newMeal = [MenuLoader MealAfterMeal:curMeal];
 MenuTableController *newMenu = [self.storyboard instantiateViewControllerWithIdentifier:@"Menu"];
 newMenu.currentMeal = newMeal;
 [self.navigationController pushViewController:newMenu animated:YES];
}

When each view controller is created (including the first), it loads the menu in a background thread:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 MenuLoader *ml = [[MenuLoader alloc] init];
 currentMenu = [ml mealForType:_currentMeal Specificity:[self summaryPreference]];
 dispatch_async(dispatch_get_main_queue(), ^(void) {
 ...
 });
 });

Inside the mealForType:Specificity: call, I parse 2 separate web pages through NSURL, and combine the information from both sites:

NSURL *url = [NSURL URLWithString:urlString];
NSData *pageData = [NSData dataWithContentsOfURL:url];

Some of the optimizations I'm hoping to make are:

  • Preloading adjacent menus in another thread to reduce loading time when someone swipes
  • If someone swipes forward and then back, it should show the already loaded menu. Right now, it makes a fresh call to the server and reloads everything.
  • Right now, if someone swipes multiple times in a row before any of the menus finish loading, the app tries processing all of the server calls at once and this slows down the loading time. If someone does swipe repeatedly, the current menu should take precedence, and possibly the other calls should be terminated.

It sounds like a big overhaul of my design, so I was wondering if there was a better approach to loading menus than what I currently implemented.

asked Dec 25, 2014 at 0:59
\$\endgroup\$
4
  • \$\begingroup\$ When you swipe left and right, are you limited to just today's meals? Or if I swipe past the last meal of today, will the app load the first meal of tomorrow? \$\endgroup\$ Commented Dec 25, 2014 at 2:13
  • \$\begingroup\$ As of now, the former. I would like to implement the latter at some point \$\endgroup\$ Commented Dec 25, 2014 at 3:44
  • \$\begingroup\$ You should use UIPageViewController. It does exactly what you're trying to accomplish manually. \$\endgroup\$ Commented Dec 26, 2014 at 22:41
  • 1
    \$\begingroup\$ Also you probably should ask this on StackOverflow, since it's more about implementation details rather then code review. \$\endgroup\$ Commented Dec 27, 2014 at 18:22

1 Answer 1

2
\$\begingroup\$

I recently created a similar layout for a project I'm working on using Apple's PageControl example as a base.

I used a UICollectionView for the top nav section and a UIScrollView for paging through view controllers. The main view controller is setup as below.

Main View Controller

In the viewDidLoad method for the I set up the content size for the scrollView and set paging to YES.

[self.containerScrollView setContentSize:CGSizeMake(CGRectGetWidth(self.containerScrollView.frame)*self.numberOfPages, 
CGRectGetHeight(self.containerScrollView.frame))];
[self.containerScrollView setPagingEnabled:YES];
[self.containerScrollView setShowsHorizontalScrollIndicator:NO];
[self.containerScrollView setShowsVerticalScrollIndicator:NO];
[self.containerScrollView setScrollsToTop:NO];
[self.containerScrollView setDelegate:self];

(Note: if you're using xibs/size classes, I ran into a problem where the scrollView's size is stated as 600x600px no matter which device is used until the ParentViewController's viewDidLayoutSubviews is called. I had to reset the scroll view's content size again.)

After this is set up, I load the first two view controllers:

- (void)loadScrollViewWithPage:(NSUInteger)page {
 UIViewController *viewController = <get your view controller>;
 if (viewController.view.superview == nil) {
 [self addChildViewController:viewController];
 CGRect frame = [self.containerScrollView frame];
 frame.origin.x = CGRectGetWidth(frame)*page;
 frame.origin.y = 0;
 [viewController.view setFrame:frame];
 [self.containerScrollView addSubview:viewController.view];
 [viewController didMoveToParentViewController:self];
 }
}

Since the first two view controllers are loaded, there is no lag when the user swipes between them. When UIScrollView is scrolled, the page is scrolls to is already loaded. Once it does, I load the next one in order:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
 CGFloat pageWidth = CGRectGetWidth(self.scrollView.frame);
 NSUInteger page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
 [self loadScrollViewWithPage:page - 1];
 [self loadScrollViewWithPage:page];
 [self loadScrollViewWithPage:page + 1];
}

This way you always have the current, previous and the next view controller always ready. You could load more than 3 if the network calls you have to make take longer and the what the user is expected to scroll at.

You could unload older view controllers by calling viewController.view = nil and always only have a set number of view controllers loaded.

I created a custom protocol for the UICollectionView and set my parent controller as delegate to respond to cell clicks and navigate to the appropriate page.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered Jul 29, 2015 at 19:35
\$\endgroup\$
1
  • \$\begingroup\$ The documentation for unloading a view controller is here. \$\endgroup\$ Commented Jul 29, 2015 at 19:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.