Parallax view scrolling (Yahoo weather like)
Asked Answered
M

3

5

This is not strictly a programming question but more "how to accomplish this" question.

I am curious (and working on an app that will probably require this) how is left-right parallax scrolling implemented. To know exactly what I mean check the Yahoo Weather app (it's free - no worries there).

Are they using just one view controller or a separate controller for each view that is shown there?
What is the easiest way to implement this? I have found this topic here which kinda explains it a bit but when are they getting information from their servers? Is it when the views are changed or at the startup of the app?

Any information how to implement such scrolling would be much appreciated.

Maniple answered 31/7, 2013 at 19:47 Comment(0)
S
5

It's really simple actually:

  • Subclass UIScrollView
  • Add a UIView *parallaxView; as @property
  • Add a CGFloat parallaxFactor as @property
  • Override layoutSubviews
  • Call super, and then use self.scrollOffset*parallaxFactor to position the parallaxView

That's it!

I've made a multifunctional UIScrollView subclass myself that's really simple to use, perfect for this case! Get it on GitHub: https://github.com/LeonardPauli/LPParallaxScrollView

Good Luck!

Sylvester answered 31/7, 2013 at 19:52 Comment(7)
Yes, well this handles the visible part indeed. But how about functionality? For example if user wants to update weather he pulls the view down and it refreshes with the latest weather info. I doubt that they use ifs to determine which view is presented. Any ideas?Maniple
I open-sourced something I made a while ago! github.com/LeonardPauli/MYParallaxScrollView Just use parallaxView.currentPageIndex :) But even better, use a different vertical (parallax for the simple block) scrollview for each page, with the reload there. (Don't forget to make the gesture recognizers block each other right) :DSylvester
I tried to make it tick today but you forgot to include UIView+myExt.h. Could you post that as well?Maniple
Oh, sorry!! Will add it immediately!Sylvester
Almost there :D now UIImage+Crop.h is missing.Maniple
Now I really hope I haven't forgot something else.. github.com/LeonardPauli/MYParallaxScrollViewSylvester
Looks like a great library. Would love to have it available on cocoapods ;)Culmination
R
0

I implemented a small prototype based on a standard UIPageViewController subclass, and autolayout constraints for the parallax effect. It's quite fully documented, if you want to have a look : TestParallax

In a nutshell, the heart of the parallax is done in the scrollViewDidScroll delegate method:

extension PageViewController: UIScrollViewDelegate {

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let screenWidth = scrollView.bounds.width
        /*
         In a UIPageViewController, the initial contentOffset.x is not 0, but equal to the screen width
         (so that the offset goes between (1 * screenWidth) and 0 when going to the previous view controller, 
         and from (1 * screenWidth) to (2 * screenWidth) when going to the next view controller).
         Also, it's reset to the screenWidth when the scroll to a previous or next view controller is complete.
         Therefore, we calculate a new 'horizontalOffset' starting at 0, and going:
         - negative from 0 to (-screenWidth/2) when scrolling to the next view controller,
         - and from 0 to (screenWidth/2) when scrolling to the previous view controller.
         */
        let horizontalOffset = (scrollView.contentOffset.x - screenWidth)/2

        // Special case: initial situation, or when the horizontalOffset is reset to 0 by the UIPageViewController.
        guard horizontalOffset != 0 else {
            previousPageController?.offsetBackgroundImage(by: screenWidth/2)
            currentPageController?.offsetBackgroundImage(by: 0)
            nextPageController?.offsetBackgroundImage(by: -screenWidth/2)
            return
        }

        // The background image of the current page controller should always be offset by the horizontalOffset (which may be positive or negative)
        guard let currentPageController = currentPageController else { return }
        currentPageController.offsetBackgroundImage(by: horizontalOffset)

        if horizontalOffset > 0 { // swiping left, to the next page controller

            // The background image of the next page controller starts with an initial offset of (-screenWidth/2), then we apply the (positive) horizontalOffset
            if let nextPageController = nextPageController {
                let nextOffset = -screenWidth/2 + horizontalOffset
                nextPageController.offsetBackgroundImage(by: nextOffset)
            }
        } else { // swiping right, to the previous page controller

            // The background image of the previous page controller starts with an initial offset of (+screenWidth/2), then we apply the (negative) horizontalOffset
            if let previousPageController = previousPageController {
                let previousOffset = screenWidth/2 + horizontalOffset
                previousPageController.offsetBackgroundImage(by: previousOffset)
            }
        }
    }
}
Rasheedarasher answered 30/12, 2018 at 18:14 Comment(0)
T
0

You are not supposed to change the delegate of the page view controller's scroll view. It can break its normal behaviour.

Instead, you can:

  1. Add a pan gesture to the page view controller's view:

    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panRecognized(gesture:)))
    view.addGestureRecognizer(panGesture)
    panGesture.delegate = self
    
  2. Add the new function in order to know how the view is being scrolled.

    @objc func panRecognized(gesture: UIPanGestureRecognizer) {
        // Do whatever you need with the gesture.translation(in: view)
    }
    
  3. Declare your ViewController as UIGestureRecognizerDelegate.

  4. Implement this function:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    
Thenna answered 9/11, 2020 at 13:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.