Static Similarity Background Image Effect

In the latest Expedia app for iOS, they have a very interesting effect that I'm trying to circle around myself. You have two columns of infinitely scrollable subzones, which, as I know, can be executed using 2 scrollviews. The interesting part is that in the general scrolling there is a background background that remains fixed and can be seen in the gap between each of the subzonal cells. The really cool part is that the subzones have a different background that stays in place. In the screenshot below, this is an image of the city skyline. When scrolling in sub-items, the image of the city can only be seen behind the subzones. This seems to be some kind of masking trick, but I can't figure out how to do it. How can I achieve the same result?

Essentially, as you can show the static background behind the subzones, which act like small windows and don't show laundry. Underwear should be displayed only around the cells.

You can download the application, press airplane mode and try it yourself.

Here is a screenshot: enter image description here

Here is another one to show that the cells scroll, but the city remains the same:

enter image description here

+4
source share
2 answers

I would like to find an elegant solution, for now I would do it by tracking the offset of the subviews visible and adjusting their appearance.

Please check the result in a sample project .

For a future reference, I will attach the following code:

ViewController.m

// // OSViewController.m // ScrollMasks // // Created by #%$^Q& on 11/30/12. // Copyright (c) 2012 Demo. All rights reserved. // #import "OSViewController.h" @interface OSViewController () // subviews @property (strong) IBOutlet UIScrollView * scrollView; // all the subviews @property (strong) NSArray * maskedSubviews; // subviews visible at scrollview, we'll update only them @property (strong) NSArray * visibleMaskedSubviews; // updates the views from visibleMaskedSubviews -(void) updateVisibleSubviews; // updates the visibleMaskedSubviews array with the given scrollView offset -(void) updateVisibleSubviewsArrayForOffset:(CGPoint) offset; @end @implementation OSViewController -(void) unused {} #pragma mark - view -(void) viewWillAppear:(BOOL)animated { [self updateVisibleSubviews]; [super viewWillAppear:animated]; } - (void)viewDidLoad { [super viewDidLoad]; /* See -updateVisibleSubviews comment for the class comments */ UIView * newMaskedView = nil; NSMutableArray * newMaskedSubviews = [NSMutableArray array]; const CGSize scrollViewSize = self.scrollView.bounds.size; const int totalSubviews = 10; const float offset = 20.; const float height = 100.; UIImage * maskImage = [UIImage imageNamed:@"PeeringFrog.jpg"]; /* // Uncomment to compare UIImageView * iv = [[UIImageView alloc] initWithFrame:self.scrollView.bounds]; iv.image = maskImage; [self.view insertSubview:iv atIndex:0]; */ // add scrollview subviews for (int i = 0; i < totalSubviews; i++) { CGRect newViewFrame = CGRectMake(offset, offset*(i+1) + height*i, scrollViewSize.width - offset*2, height); newMaskedView = [UIView new]; newMaskedView.frame = newViewFrame; newMaskedView.clipsToBounds = YES; newMaskedView.backgroundColor = [UIColor redColor]; newMaskedView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth; UIImageView * maskImageView = [UIImageView new]; maskImageView.frame = CGRectMake(0, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height); maskImageView.image = maskImage; [newMaskedView addSubview:maskImageView]; [self.scrollView addSubview:newMaskedView]; [newMaskedSubviews addObject:newMaskedView]; } self.scrollView.contentSize = CGSizeMake(scrollViewSize.width, (height+offset)*totalSubviews + offset*2); self.maskedSubviews = [NSArray arrayWithArray:newMaskedSubviews]; [self updateVisibleSubviewsArrayForOffset:self.scrollView.contentOffset]; } -(void) updateVisibleSubviews { [self updateVisibleSubviewsArrayForOffset:self.scrollView.contentOffset]; for (UIView * view in self.visibleMaskedSubviews) { /* TODO: view must be UIView subclass with the imageView initializer and imageView frame update method */ CGPoint viewOffset = [self.view convertPoint:CGPointZero fromView:view]; UIImageView * subview = [[view subviews] objectAtIndex:0]; CGRect subviewFrame = subview.frame; subviewFrame = CGRectMake(-viewOffset.x, -viewOffset.y, subviewFrame.size.width, subviewFrame.size.height); subview.frame = subviewFrame; } } #pragma mark - scrollview delegate & utilities -(void) scrollViewDidScroll:(UIScrollView *)scrollView { [self updateVisibleSubviews]; } -(void) updateVisibleSubviewsArrayForOffset:(CGPoint) offset { NSMutableArray * newVisibleMaskedSubviews = [NSMutableArray array]; for (UIView * view in self.maskedSubviews) { CGRect intersectionRect = CGRectIntersection(view.frame, self.scrollView.bounds); if (NO == CGRectIsNull(intersectionRect)) { [newVisibleMaskedSubviews addObject:view]; } } self.visibleMaskedSubviews = [NSArray arrayWithArray:newVisibleMaskedSubviews]; } #pragma mark - memory - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end 

ViewController.h

 // // OSViewController.h // ScrollMasks // // Created by #%$^Q& on 11/30/12. // Copyright (c) 2012 Demo. All rights reserved. // /* PeeringFrog image is taken (and resized) from Apple sample project "PhotoScroller" */ #import <UIKit/UIKit.h> @interface OSViewController : UIViewController <UIScrollViewDelegate> @end 
+1
source

I did something similar a few years ago. At first I tried to use the CGImageMaskCreate material, but it was much simpler just to create an image with transparent β€œcutouts”, and then use the animation effects to scroll the images under it.

In your case, I would find a screen-sized image of laundry. Then I used the image editor (I use GIMP) to draw a number of drawers on the laundry using a flat color. Then I outlined this color box transparent to make cutouts. There are other ways to do this, but the way I do it.

In the application, add two or more images to the main screen. Do not worry about placement, because it will be determined at runtime. You want these image views to contain the images you want to scroll. Then add your linen-with-cutout UIImageView so that it is on top and it occupies the entire screen size. Make sure the top background of the UIImageView is set to transparent.

When the application starts, place your β€œbottom” images, top to bottom, and then run [UIView beginAnimation], which scrolls the images under the images up, changing the β€œy” position. This animation should have a callback made, which is called when the top view of the image is completely disconnected from the screen. Then, in the callback made, return the current state and start the animation again. Here are the guts of the code I used (but note that my scroll was from right to left, not from bottom to top, and my images were the same size.)

  - (void)doAnimationSet { [iv1 setFrame:CGRectMake(0, 0, imageWidth, imageHeight)]; [iv2 setFrame:CGRectMake(imageWidth, 0, imageWidth, imageHeight)]; [iv3 setFrame:CGRectMake(imageWidth*2, 0, imageWidth, imageHeight)]; [self loadNextImageSet]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:10]; [UIView setAnimationCurve:UIViewAnimationCurveLinear]; [iv1 setFrame:CGRectMake(-imageWidth, 0, imageWidth, imageHeight)]; [iv2 setFrame:CGRectMake(0, 0, imageWidth, imageHeight)]; [iv3 setFrame:CGRectMake(imageWidth, 0, imageWidth, imageHeight)]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(doneAnimation:finished:context:)]; [UIView commitAnimations]; } - (void)doneAnimation:(NSString *)aid finished:(BOOL)fin context:(void *)context { [self doAnimationSet]; } 

This should give you the effect you are looking for. Good luck :)

0
source

All Articles