From 547e0e024e6f5b8cb1c04cd0fc17ba5b5c128a2e Mon Sep 17 00:00:00 2001 From: Patryk Klatka Date: Tue, 30 Apr 2024 23:14:57 +0200 Subject: [PATCH] iOS: introduce `refreshControlTopAnchor` property (#203) --- packages/turbo/README.md | 6 +++ packages/turbo/ios/RNVisitableView.swift | 5 +++ packages/turbo/ios/RNVisitableViewManager.m | 1 + packages/turbo/patches/turbo-ios.patch | 41 +++++++++++++-------- packages/turbo/src/RNVisitableView.ts | 1 + packages/turbo/src/VisitableView.tsx | 3 ++ 6 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/turbo/README.md b/packages/turbo/README.md index 6e482c34..7c66817f 100644 --- a/packages/turbo/README.md +++ b/packages/turbo/README.md @@ -97,6 +97,12 @@ The amount by which the web view content is inset from the edges of the scroll v Note: available only on iOS. +### `refreshControlTopAnchor` + +This property enables setting custom `topAnchor` for the native refresh control. If the value is set, the refresh control will be anchored to the top of the web view with the specified offset. By default, this value is set to the safe area top anchor. + +Note: available only on iOS. + ### `progressViewOffset` The refresh indicator starting and resting position is always positioned near the top of the refreshing content. This position is a consistent location, but can be adjusted in either direction based on whether or not there is a header or other content that should be visible when the refresh indicator is shown. diff --git a/packages/turbo/ios/RNVisitableView.swift b/packages/turbo/ios/RNVisitableView.swift index 07ccb2e2..6b49e481 100644 --- a/packages/turbo/ios/RNVisitableView.swift +++ b/packages/turbo/ios/RNVisitableView.swift @@ -25,6 +25,11 @@ class RNVisitableView: UIView, RNSessionSubscriber { controller!.visitableView.allowsPullToRefresh = pullToRefreshEnabled } } + @objc var refreshControlTopAnchor: NSNumber = 0 { + didSet { + controller!.visitableView.refreshControlTopAnchor = refreshControlTopAnchor as! CGFloat + } + } @objc var scrollEnabled: Bool = true { didSet { configureWebView() diff --git a/packages/turbo/ios/RNVisitableViewManager.m b/packages/turbo/ios/RNVisitableViewManager.m index 2ffa7418..52fd3efe 100644 --- a/packages/turbo/ios/RNVisitableViewManager.m +++ b/packages/turbo/ios/RNVisitableViewManager.m @@ -19,6 +19,7 @@ @interface RCT_EXTERN_MODULE(RNVisitableViewManager, NSObject) RCT_EXPORT_VIEW_PROPERTY(pullToRefreshEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(contentInset, NSDictionary) + RCT_EXPORT_VIEW_PROPERTY(refreshControlTopAnchor, NSNumber) RCT_EXPORT_VIEW_PROPERTY(webViewDebuggingEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(onVisitProposal, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onOpenExternalUrl, RCTDirectEventBlock) diff --git a/packages/turbo/patches/turbo-ios.patch b/packages/turbo/patches/turbo-ios.patch index 06fb01fa..48a72f71 100644 --- a/packages/turbo/patches/turbo-ios.patch +++ b/packages/turbo/patches/turbo-ios.patch @@ -1,21 +1,32 @@ diff --git a/Source/Visitable/VisitableView.swift b/Source/Visitable/VisitableView.swift -index 12452b5..e9c37b8 100644 +index 12452b5..49b932c 100644 --- a/Source/Visitable/VisitableView.swift +++ b/Source/Visitable/VisitableView.swift -@@ -69,16 +69,6 @@ open class VisitableView: UIView { +@@ -16,6 +16,8 @@ open class VisitableView: UIView { + installActivityIndicatorView() + } + ++ public var refreshControlTopAnchor: CGFloat = 0 ++ + // MARK: Web View + + open var webView: WKWebView? +@@ -74,9 +76,17 @@ open class VisitableView: UIView { + /// Otherwise fallback to 60 (the default height). + let refreshControlHeight = refreshControl.frame.height > 0 ? refreshControl.frame.height : 60 - #if !targetEnvironment(macCatalyst) - scrollView.addSubview(refreshControl) -- -- /// Infer refresh control's default height from its frame, if given. -- /// Otherwise fallback to 60 (the default height). -- let refreshControlHeight = refreshControl.frame.height > 0 ? refreshControl.frame.height : 60 -- -- NSLayoutConstraint.activate([ -- refreshControl.centerXAnchor.constraint(equalTo: centerXAnchor), ++ let topAnchorConstraint: NSLayoutConstraint ++ ++ if (refreshControlTopAnchor > 0) { ++ topAnchorConstraint = refreshControl.topAnchor.constraint(equalTo: webView!.topAnchor, constant: refreshControlTopAnchor) ++ } else { ++ topAnchorConstraint = refreshControl.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor) ++ } ++ + NSLayoutConstraint.activate([ ++ topAnchorConstraint, + refreshControl.centerXAnchor.constraint(equalTo: centerXAnchor), - refreshControl.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor), -- refreshControl.heightAnchor.constraint(equalToConstant: refreshControlHeight) -- ]) + refreshControl.heightAnchor.constraint(equalToConstant: refreshControlHeight) + ]) #endif - } - diff --git a/packages/turbo/src/RNVisitableView.ts b/packages/turbo/src/RNVisitableView.ts index 9402dc24..450ee425 100644 --- a/packages/turbo/src/RNVisitableView.ts +++ b/packages/turbo/src/RNVisitableView.ts @@ -32,6 +32,7 @@ export interface RNVisitableViewProps { scrollEnabled: boolean; contentInset: ContentInsetObject; progressViewOffset?: ProgressViewOffsetObject; + refreshControlTopAnchor: number; webViewDebuggingEnabled: boolean; onLoad?: (e: NativeSyntheticEvent) => void; onMessage?: (e: NativeSyntheticEvent) => void; diff --git a/packages/turbo/src/VisitableView.tsx b/packages/turbo/src/VisitableView.tsx index 287d65fe..c16bb381 100644 --- a/packages/turbo/src/VisitableView.tsx +++ b/packages/turbo/src/VisitableView.tsx @@ -54,6 +54,7 @@ export interface Props { scrollEnabled?: boolean; contentInset?: ContentInsetObject; progressViewOffset?: ProgressViewOffsetObject; + refreshControlTopAnchor?: number; webViewDebuggingEnabled?: boolean; renderLoading?: RenderLoading; renderError?: RenderError; @@ -87,6 +88,7 @@ const VisitableView = React.forwardRef>( scrollEnabled = true, contentInset = { top: 0, left: 0, right: 0, bottom: 0 }, progressViewOffset, + refreshControlTopAnchor = 0, webViewDebuggingEnabled = false, renderLoading, renderError, @@ -227,6 +229,7 @@ const VisitableView = React.forwardRef>( scrollEnabled={scrollEnabled} contentInset={contentInset} progressViewOffset={progressViewOffset} + refreshControlTopAnchor={refreshControlTopAnchor} webViewDebuggingEnabled={webViewDebuggingEnabled} onError={onErrorCombinedHandlers} onVisitProposal={handleVisitProposal}