diff --git a/modules/expo-scroll-forwarder/expo-module.config.json b/modules/expo-scroll-forwarder/expo-module.config.json deleted file mode 100644 index 1fd49f79b7..0000000000 --- a/modules/expo-scroll-forwarder/expo-module.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": ["ios"], - "ios": { - "modules": ["ExpoScrollForwarderModule"] - } -} diff --git a/modules/expo-scroll-forwarder/index.ts b/modules/expo-scroll-forwarder/index.ts deleted file mode 100644 index a4ad4b8506..0000000000 --- a/modules/expo-scroll-forwarder/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {ExpoScrollForwarderView} from './src/ExpoScrollForwarderView' diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec deleted file mode 100644 index 78ca9812e4..0000000000 --- a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarder.podspec +++ /dev/null @@ -1,21 +0,0 @@ -Pod::Spec.new do |s| - s.name = 'ExpoScrollForwarder' - s.version = '1.0.0' - s.summary = 'Forward scroll gesture from UIView to UIScrollView' - s.description = 'Forward scroll gesture from UIView to UIScrollView' - s.author = 'bluesky-social' - s.homepage = 'https://github.com/bluesky-social/social-app' - s.platforms = { :ios => '13.4', :tvos => '13.4' } - s.source = { git: '' } - s.static_framework = true - - s.dependency 'ExpoModulesCore' - - # Swift/Objective-C compatibility - s.pod_target_xcconfig = { - 'DEFINES_MODULE' => 'YES', - 'SWIFT_COMPILATION_MODE' => 'wholemodule' - } - - s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" -end diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift deleted file mode 100644 index 53e2588258..0000000000 --- a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderModule.swift +++ /dev/null @@ -1,13 +0,0 @@ -import ExpoModulesCore - -public class ExpoScrollForwarderModule: Module { - public func definition() -> ModuleDefinition { - Name("ExpoScrollForwarder") - - View(ExpoScrollForwarderView.self) { - Prop("scrollViewTag") { (view: ExpoScrollForwarderView, prop: Int) in - view.scrollViewTag = prop - } - } - } -} diff --git a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift b/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift deleted file mode 100644 index e6ebef1d33..0000000000 --- a/modules/expo-scroll-forwarder/ios/ExpoScrollForwarderView.swift +++ /dev/null @@ -1,230 +0,0 @@ -import ExpoModulesCore - -// This view will be used as a native component. Make sure to inherit from `ExpoView` -// to apply the proper styling (e.g. border radius and shadows). -class ExpoScrollForwarderView: ExpoView, UIGestureRecognizerDelegate { - var scrollViewTag: Int? { - didSet { - self.tryFindScrollView() - } - } - - private var scrollView: UIScrollView? - private var rctRefreshCtrl: RCTRefreshControl? - private var cancelGestureRecognizers: [UIGestureRecognizer]? - private var animTimer: Timer? - private var initialOffset: CGFloat = 0.0 - private var didImpact: Bool = false - - required init(appContext: AppContext? = nil) { - super.init(appContext: appContext) - - let pg = UIPanGestureRecognizer(target: self, action: #selector(callOnPan(_:))) - pg.delegate = self - self.addGestureRecognizer(pg) - - let tg = UITapGestureRecognizer(target: self, action: #selector(callOnPress(_:))) - tg.isEnabled = false - tg.delegate = self - - let lpg = UILongPressGestureRecognizer(target: self, action: #selector(callOnPress(_:))) - lpg.minimumPressDuration = 0.01 - lpg.isEnabled = false - lpg.delegate = self - - self.cancelGestureRecognizers = [lpg, tg] - } - - // We don't want to recognize the scroll pan gesture and the swipe back gesture together - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - if gestureRecognizer is UIPanGestureRecognizer, otherGestureRecognizer is UIPanGestureRecognizer { - return false - } - - return true - } - - // We only want the "scroll" gesture to happen whenever the pan is vertical, otherwise it will - // interfere with the native swipe back gesture. - override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - guard let gestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer else { - return true - } - - let velocity = gestureRecognizer.velocity(in: self) - return abs(velocity.y) > abs(velocity.x) - } - - // This will be used to cancel the scroll animation whenever we tap inside of the header. We don't need another - // recognizer for this one. - override func touchesBegan(_ touches: Set, with event: UIEvent?) { - self.stopTimer() - } - - // This will be used to cancel the animation whenever we press inside of the scroll view. We don't want to change - // the scroll view gesture's delegate, so we add an additional recognizer to detect this. - @IBAction func callOnPress(_ sender: UITapGestureRecognizer) { - self.stopTimer() - } - - @IBAction func callOnPan(_ sender: UIPanGestureRecognizer) { - guard let sv = self.scrollView else { - return - } - - let translation = sender.translation(in: self).y - - if sender.state == .began { - if sv.contentOffset.y < 0 { - sv.contentOffset.y = 0 - } - - self.initialOffset = sv.contentOffset.y - } - - if sender.state == .changed { - sv.contentOffset.y = self.dampenOffset(-translation + self.initialOffset) - - if sv.contentOffset.y <= -130, !didImpact { - let generator = UIImpactFeedbackGenerator(style: .light) - generator.impactOccurred() - - self.didImpact = true - } - } - - if sender.state == .ended { - let velocity = sender.velocity(in: self).y - self.didImpact = false - - if sv.contentOffset.y <= -130 { - self.rctRefreshCtrl?.forwarderBeginRefreshing() - return - } - - // A check for a velocity under 250 prevents animations from occurring when they wouldn't in a normal - // scroll view - if abs(velocity) < 250, sv.contentOffset.y >= 0 { - return - } - - self.startDecayAnimation(translation, velocity) - } - } - - func startDecayAnimation(_ translation: CGFloat, _ velocity: CGFloat) { - guard let sv = self.scrollView else { - return - } - - var velocity = velocity - - self.enableCancelGestureRecognizers() - - if velocity > 0 { - velocity = min(velocity, 5000) - } else { - velocity = max(velocity, -5000) - } - - var animTranslation = -translation - self.animTimer = Timer.scheduledTimer(withTimeInterval: 1.0 / 120, repeats: true) { _ in - velocity *= 0.9875 - animTranslation = (-velocity / 120) + animTranslation - - let nextOffset = self.dampenOffset(animTranslation + self.initialOffset) - - if nextOffset <= 0 { - if self.initialOffset <= 1 { - self.scrollToOffset(0) - } else { - sv.contentOffset.y = 0 - } - - self.stopTimer() - return - } else { - sv.contentOffset.y = nextOffset - } - - if abs(velocity) < 5 { - self.stopTimer() - } - } - } - - func dampenOffset(_ offset: CGFloat) -> CGFloat { - if offset < 0 { - return offset - (offset * 0.55) - } - - return offset - } - - private func findScrollView(in view: UIView, foundCount: Int) -> UIScrollView? { - var foundCount = foundCount - if let sv = view as? UIScrollView { return sv } - for child in view.subviews { - if let found = findScrollView(in: child, foundCount: foundCount) { - if foundCount == 1 { - print("found sv: \(found)") -// return found - } else { - print("found sv: \(found)") - foundCount += 1 - } - } - } - return nil - } - - func tryFindScrollView() { - // Before we switch to a different scrollview, we always want to remove the cancel gesture recognizer. - // Otherwise we might end up with duplicates when we switch back to that scrollview. - self.removeCancelGestureRecognizers() - - guard let sv = self.findScrollView(in: self.superview!.superview!.superview!, foundCount: 0) else { - print("⚠️ ExpoScrollForwarder: couldn’t find UIScrollView under tag \(tag)") - return - } - - self.scrollView = sv - self.rctRefreshCtrl = sv.refreshControl as? RCTRefreshControl - - self.addCancelGestureRecognizers() - } - - func addCancelGestureRecognizers() { - self.cancelGestureRecognizers?.forEach { r in - self.scrollView?.addGestureRecognizer(r) - } - } - - func removeCancelGestureRecognizers() { - self.cancelGestureRecognizers?.forEach { r in - self.scrollView?.removeGestureRecognizer(r) - } - } - - func enableCancelGestureRecognizers() { - self.cancelGestureRecognizers?.forEach { r in - r.isEnabled = true - } - } - - func disableCancelGestureRecognizers() { - self.cancelGestureRecognizers?.forEach { r in - r.isEnabled = false - } - } - - func scrollToOffset(_ offset: Int, animated: Bool = true) { - self.scrollView?.scrollRectToVisible(CGRect(x: 0, y: offset, width: 0, height: 0), animated: animated) - } - - func stopTimer() { - self.disableCancelGestureRecognizers() - self.animTimer?.invalidate() - self.animTimer = nil - } -} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts b/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts deleted file mode 100644 index 3f1e4a63da..0000000000 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarder.types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ExpoScrollForwarderViewProps { - scrollViewTag: number | null - children: React.ReactNode -} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx deleted file mode 100644 index 18bdb25c8a..0000000000 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.ios.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import {requireNativeViewManager} from 'expo-modules-core' - -import {type ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' - -const NativeView: React.ComponentType = - requireNativeViewManager('ExpoScrollForwarder') - -export function ExpoScrollForwarderView({ - children, - ...rest -}: ExpoScrollForwarderViewProps) { - return {children} -} diff --git a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx b/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx deleted file mode 100644 index d66eef7cbd..0000000000 --- a/modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import {type ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' - -export function ExpoScrollForwarderView({ - children, -}: React.PropsWithChildren) { - return children -} diff --git a/modules/react-native-scroll-forwarder/ios/ScrollForwarderView.mm b/modules/react-native-scroll-forwarder/ios/ScrollForwarderView.mm index 79422a4e17..0af029626c 100644 --- a/modules/react-native-scroll-forwarder/ios/ScrollForwarderView.mm +++ b/modules/react-native-scroll-forwarder/ios/ScrollForwarderView.mm @@ -77,6 +77,7 @@ static const CGFloat kMinimumVelocity = 5.0; - (void)dealloc { [self stopAnimation]; + [self removeCancelGestureRecognizers]; _svcv = nil; } @@ -84,6 +85,7 @@ static const CGFloat kMinimumVelocity = 5.0; { [super prepareForRecycle]; [self stopAnimation]; + [self removeCancelGestureRecognizers]; _svcv = nil; } @@ -209,10 +211,6 @@ static const CGFloat kMinimumVelocity = 5.0; if (sv.contentOffset.y <= -kPullThreshold) { [self refresh]; -// if ([_svcv.scrollView.refreshControl isKindOfClass:[RCTRefreshControl class]]) { -// RCTRefreshControl *refreshCtrl = (RCTRefreshControl *) _svcv.scrollView.refreshControl; -// [refreshCtrl forwarderBeginRefreshing]; -// } return; } @@ -353,12 +351,9 @@ static const CGFloat kMinimumVelocity = 5.0; return nil; } -- (void) refresh +- (void)refresh { -// if ([_svcv.scrollView.refreshControl isKindOfClass:[RCTPullToRefreshViewComponentView class]]) { -// RCTPullToRefreshViewComponentView *refreshView = (RCTPullToRefreshViewComponentView *) _svcv.scrollView.refreshControl; -// [refreshView beginRefreshingProgrammatically]; -// } + // TODO: implement } Class ScrollForwarderViewCls(void) @@ -367,35 +362,3 @@ Class ScrollForwarderViewCls(void) } @end - - -//- (void)forwarderBeginRefreshing -//{ -// _refreshingProgrammatically = NO; -// -// [self sizeToFit]; -// -// if (!self.scrollView) { -// return; -// } -// -// UIScrollView *scrollView = (UIScrollView *)self.scrollView; -// -// [UIView animateWithDuration:0.3 -// delay:0 -// options:UIViewAnimationOptionBeginFromCurrentState -// animations:^(void) { -// // Whenever we call this method, the scrollview will always be at a position of -// // -130 or less. Scrolling back to -65 simulates the default behavior of RCTRefreshControl -// [scrollView setContentOffset:CGPointMake(0, -65)]; -// } -// completion:^(__unused BOOL finished) { -// [super beginRefreshing]; -// [self setCurrentRefreshingState:super.refreshing]; -// -// if (self->_onRefresh) { -// self->_onRefresh(nil); -// } -// } -// ]; -//}