DEV Community

Cover image for Effortless Parallax in React Native
Muhammad haris baig
Muhammad haris baig

Posted on • Edited on

Effortless Parallax in React Native

Hey everyone! If you're searching for a painless and straight forward solution to your parallax issues, look no further! I've got a perfect, easy-to-digest solution for creating a parallax effect with a sticky header, a hide-able parallax view, and a scrollable container that activates once the parent scroll is finished.

The Problem:

Creating a parallax effect in React Native can be tricky, especially when trying to synchronize scrolling between a parent view and nested views. Common issues include the parallax view not hiding correctly, the child scroll view not activating at the right time, and the sticky header not behaving as expected.

The Solution:

The following code solves these problems by using a combination of React Native and react-native-reanimated to manage the scroll events efficiently. The solution ensures that the sticky header remains in place, the parallax view hides as the user scrolls up, and the nested scroll view becomes scrollable when the parent scroll is completed.

Here's the Code:

You can copy and paste the following code to get started:

import Foundation
import PassKit
import React
struct PaymentRequestParams: Codable {
let merchantIdentifier: String
let supportedNetworks: [String]
let countryCode: String
let currencyCode: String
let label: String
let amount: String
}
@objc(ApplePayModule)
class ApplePayModule: NSObject {
private var paymentResolver: RCTPromiseResolveBlock?
private var paymentRejecter: RCTPromiseRejectBlock?
private var completionHandler: ((PKPaymentAuthorizationResult) -> Void)?
@objc
static func requiresMainQueueSetup() -> Bool {
return true
}
@objc
func canMakePayments(_ resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock) {
resolver(PKPaymentAuthorizationController.canMakePayments())
}
@objc
func requestPayment(_ paramsJSON: NSDictionary, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
guard let jsonData = try? JSONSerialization.data(withJSONObject: paramsJSON, options: []),
let params = try? JSONDecoder().decode(PaymentRequestParams.self, from: jsonData) else {
rejecter("E_INVALID_PARAMS", "Invalid payment request parameters", nil)
return
}
let paymentRequest = PKPaymentRequest()
paymentRequest.merchantIdentifier = params.merchantIdentifier
paymentRequest.supportedNetworks = mapSupportedNetworks(params.supportedNetworks)
paymentRequest.merchantCapabilities = .capability3DS
paymentRequest.countryCode = params.countryCode
paymentRequest.currencyCode = params.currencyCode
paymentRequest.paymentSummaryItems = [
PKPaymentSummaryItem(label: params.label, amount: NSDecimalNumber(string: params.amount))
]
let paymentAuthorizationController = PKPaymentAuthorizationController(paymentRequest: paymentRequest)
paymentAuthorizationController.delegate = self
paymentAuthorizationController.present { (presented: Bool) in
if !presented {
rejecter("E_PAYMENT_ERROR", "Unable to present Apple Pay authorization.", nil)
} else {
self.paymentResolver = resolver
self.paymentRejecter = rejecter
}
}
}
@objc
func completePayment(_ success: Bool) {
guard let completionHandler = self.completionHandler else {
return
}
let status: PKPaymentAuthorizationStatus = success ? .success : .failure
let result = PKPaymentAuthorizationResult(status: status, errors: nil)
completionHandler(result)
self.completionHandler = nil
}
private func mapSupportedNetworks(_ networks: [String]) -> [PKPaymentNetwork] {
return networks.compactMap { network in
switch network.lowercased() {
case "visa":
return .visa
case "mastercard":
return .masterCard
case "mada":
return .mada
case "amex":
return .amex
case "discover":
return .discover
case "jcb":
return .JCB
case "maestro":
return .maestro
case "electron":
return .electron
case "vpay":
return .vPay
default:
return nil
}
}
}
}
extension ApplePayModule: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(_ controller: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) {
// Store the completion handler
self.completionHandler = completion
// Handle the payment authorization
if let resolver = self.paymentResolver {
do {
let paymentData = try JSONSerialization.jsonObject(with: payment.token.paymentData, options: []) as? [String: Any] ?? [:]
resolver(["status": "success", "paymentData": paymentData])
} catch {
if let rejecter = self.paymentRejecter {
rejecter("APPLE_PAY_PAYMENT_REJECTED", "Payment was rejected", error)
}
completion(PKPaymentAuthorizationResult(status: .failure, errors: nil))
return
}
self.paymentResolver = nil
self.paymentRejecter = nil
} else {
// Resolver is nil, reject the payment
if let rejecter = self.paymentRejecter {
rejecter("APPLE_PAY_PAYMENT_REJECTED", "Payment was rejected", nil)
self.paymentResolver = nil
self.paymentRejecter = nil
}
completion(PKPaymentAuthorizationResult(status: .failure, errors: nil))
}
}
func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) {
controller.dismiss {
// Handle the dismissal of the payment sheet
if let rejecter = self.paymentRejecter {
rejecter("APPLE_PAY_PAYMENT_CANCELLED", "Payment was cancelled", nil)
self.paymentResolver = nil
self.paymentRejecter = nil
}
}
}
}

How It Works:

Sticky Header: The header changes color based on the scroll position, remaining visible at the top of the screen until the user scrolls to the bottom of the parent view.
Parallax View: The parallax section hides as the user scrolls up, creating a smooth transition effect.
Scrollable Container: The nested scroll view becomes scrollable only after the parent scroll view reaches the bottom, ensuring a seamless user experience.

This solution effectively manages the scrolling behavior between the parent and child views, providing a smooth and visually appealing parallax effect. Feel free to use and modify the code as needed for your projects!

Happy coding! 🎉

Image description

Please consider sharing your experiences and improvements in the comments below. Let's keep the learning going!

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more