Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallax effect with UIScrollView subviews

I'm trying to create a Parallax effect on a UIView inside a UIScrollView. The effect seems to work, but not so well.

  1. First i add two UIView sub-views to a UIScrollView and set the UIScrollViews contentSize.
  2. The Views sum up and create a contentSize of {320, 1000}.
  3. Then I implemented the following in scrollViewDidScroll:

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        CGFloat offsetY = scrollView.contentOffset.y;
    
        CGFloat percentage = offsetY / scrollView.contentSize.height;
    
        NSLog(@"percent = %f", percentage);
    
        if (offsetY < 0) {
    
            firstView.center = CGPointMake(firstView.center.x, firstView.center.y - percentage * 10);
    
        } else if (offsetY > 0){
    
            firstView.center = CGPointMake(firstView.center.x, firstView.center.y + percentage * 10);
    
        }
    }
    

These lines of code do create a parallax effect, but as the scrolling continues, the view does not return to it's original position if i scroll to the original starting position.

I have tried manipulating the views layers and frame, all with the same results.

Any Help will be much appreciated.

like image 970
Raz Avatar asked Mar 05 '14 15:03

Raz


2 Answers

The problem you have is that you are basing your secondary scrolling on a ratio of offset to size, not just on the current offset. So when you increase from an offset of 99 to 100 (out of say 100) your secondary scroll increases by 10, but when you go back down to 99 your secondary scroll only decreases by 9.9, and is thereby no longer in the same spot as it was last time you were at 99. Non-linear scrolling is possible, but not the way you are doing it.

A possible easier way to deal with this is to create a second scrollview and place it below your actual scrollview. Make it non intractable (setUserInteractionEnabled:false) and modify it's contentOffset during the main scrolling delegate instead of trying to move a UIImageView manually.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    [scrollView2 setContentOffset:CGPointMake(scrollView.contentOffset.x,scrollView.contentOffset.y * someScalingFactor) animated:NO];
}

But make sure not to set a delegate for the scrollView2, otherwise you may get a circular delegate method call that will not end well for you.

like image 143
Putz1103 Avatar answered Nov 17 '22 16:11

Putz1103


Scaling Factor being the key element...

...let me offer a 1:1 calculation:

Assuming 2 UIScrollView, one in the foreground and on in the rear, assuming the foreground controls the rear, and further assuming that a full width in the foreground corresponds to a full width in the background, you then need to apply the fore ratio, not the fore offset.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let foreSpan = foreScrolView.bounds.width - foreScrolView.contentSize.width
    let foreRatio = scrollView.contentOffset.x / foreSpan
    let rearSpan = rearScrollView.bounds.width - rearScrollView.contentSize.width
    rearScrollView.setContentOffset(
        CGPoint(x: foreRatio * rearSpan, y: 0),
        animated: false)
}

Final effect

Parallax effect

The two scrollers, fore and rear, each contain a UIImageView displayed at its full width:

let foreImg = UIImageView.init(image: UIImage(named: "fore"))
foreImg.frame = CGRect(x: 0, y: 0,
                       width: foreImg.frame.width,
                       height: foreScrolView.bounds.height)
foreScrolView.contentSize = foreImg.frame.size
foreScrolView.addSubview(foreImg)

let rearImg = UIImageView.init(image: UIImage(named: "rear"))
rearImg.frame = CGRect(x: 0, y: 0,
                       width: rearImg.frame.width,
                       height: rearScrollView.bounds.height)
rearScrollView.contentSize = rearImg.frame.size
rearScrollView.addSubview(rearImg)

This will scroll both images at a different speed, covering each image in full from edge to edge.


► Find this solution on GitHub and additional details on Swift Recipes.

like image 39
SwiftArchitect Avatar answered Nov 17 '22 15:11

SwiftArchitect