8000 fix(refresher): native ios refresher works on iPadOS (#28620) · ionic-team/ionic-framework@e522601 · GitHub
[go: up one dir, main page]

Skip to content

Commit e522601

Browse files
liamdebeasiSean Perkins
and
Sean Perkins
authored
fix(refresher): native ios refresher works on iPadOS (#28620)
Issue number: resolves #28617 --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? <!-- Please describe the current behavior that you are modifying. --> We currently check to see if `webkitOverflowScrolling` is supported on the refresher's style object in order to enable to native iOS refresher. This works well for iOS, but it does not work for iPadOS. This is because this property was removed in iPadOS 13: https://developer.apple.com/documentation/safari-release-notes/safari-13-release-notes > Disabled -webkit-overflow-scrolling: touch on iPad. All frames and scrollable overflow areas now use accelerated one-finger scrolling without changing stacking. As a result, the native iOS refresher does not activate on iPadOS. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - I think it's safe to assume that `webkitOverflowScrolling` may be removed on iOS in the future too since it was already removed on iPadOS. As a result, I implemented a solution that avoids checking this. - The `CSS.supports` check is required because otherwise the native iOS refresher would be activated in an emulated environment such as Chrome dev tools because the user agent is spoofed. The `apple-pay-logo-black` named image is only supported on Apple devices. Risks: - Apple could remove the `apple-pay-logo-black` named image in the future. However, we currently use this check elsewhere in Ionic too and it has worked well: https://github.com/ionic-team/ionic-framework/blob/60303aad23f823488afc8f8824e9c72e3ab86acc/core/src/components/datetime/datetime.ios.scss#L177. - Apple could add touch emulation to desktop Safari which could cause the native refresher to activate when using responsive design mode for testing. However, this would only impact app developer and would not impact production use cases. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Dev build: `7.5.8-dev.11703088210.14a72b83` Co-authored-by: Sean Perkins <sean-perkins@users.noreply.github.com> --------- Co-authored-by: Sean Perkins <sean-perkins@user.noreply.github.com>
1 parent 2f99aea commit e522601

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

core/src/components/refresher-content/refresher-content.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { ComponentInterface } from '@stencil/core';
22
import { Component, Element, Host, Prop, h } from '@stencil/core';
33
import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config';
4-
import { isPlatform } from '@utils/platform';
54
import { sanitizeDOMString } from '@utils/sanitization';
65
import { arrowDown, caretBackSharp } from 'ionicons/icons';
76

87
import { config } from '../../global/config';
98
import { getIonMode } from '../../global/ionic-global';
109
import type { IonicSafeString } from '../../utils/sanitization';
10+
import { supportsRubberBandScrolling } from '../refresher/refresher.utils';
1111
import type { SpinnerTypes } from '../spinner/spinner-configs';
1212
import { SPINNERS } from '../spinner/spinner-configs';
1313

@@ -63,11 +63,17 @@ export class RefresherContent implements ComponentInterface {
6363

6464
componentWillLoad() {
6565
if (this.pullingIcon === undefined) {
66+
/**
67+
* The native iOS refresher uses a spinner instead of
68+
* an icon, so we need to see if this device supports
69+
* the native iOS refresher.
70+
*/
71+
const hasRubberBandScrolling = supportsRubberBandScrolling();
6672
const mode = getIonMode(this);
67-
const overflowRefresher = (this.el.style as any).webkitOverflowScrolling !== undefined ? 'lines' : arrowDown;
73+
const overflowRefresher = hasRubberBandScrolling ? 'lines' : arrowDown;
6874
this.pullingIcon = config.get(
6975
'refreshingIcon',
70-
mode === 'ios' && isPlatform('mobile') ? config.get('spinner', overflowRefresher) : 'circular'
76+
mode === 'ios' && hasRubberBandScrolling ? config.get('spinner', overflowRefresher) : 'circular'
7177
);
7278
}
7379
if (this.refreshingSpinner === undefined) {

core/src/components/refresher/refresher.utils.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { writeTask } from '@stencil/core';
22
import { createAnimation } from '@utils/animation/animation';
33
import { clamp, componentOnReady, transitionEndAsync } from '@utils/helpers';
4-
import { isPlatform } from '@utils/platform';
54

65
// MD Native Refresher
76
// -----------------------------
@@ -195,6 +194,25 @@ export const translateElement = (el?: HTMLElement, value?: string, duration = 20
195194
// Utils
196195
// -----------------------------
197196

197+
/**
198+
* In order to use the native iOS refresher the device must support rubber band scrolling.
199+
* As part of this, we need to exclude Desktop Safari because it has a slightly different rubber band effect that is not compatible with the native refresher in Ionic.
200+
*
201+
* We also need to be careful not to include devices that spoof their user agent.
202+
* For example, when using iOS emulation in Chrome the user agent will be spoofed such that
203+
* navigator.maxTouchPointer > 0. To work around this,
204+
* we check to see if the apple-pay-logo is supported as a named image which is only
205+
* true on Apple devices.
206+
*
207+
* We previously checked referencEl.style.webkitOverflowScrolling to explicitly check
208+
* for rubber band support. However, this property was removed on iPadOS and it's possible
209+
* that this will be removed on iOS in the future too.
210+
*
211+
*/
212+
export const supportsRubberBandScrolling = () => {
213+
return navigator.maxTouchPoints > 0 && CSS.supports('background: -webkit-named-image(apple-pay-logo-black)');
214+
};
215+
198216
export const shouldUseNativeRefresher = async (referenceEl: HTMLIonRefresherElement, mode: string) => {
199217
const refresherContent = referenceEl.querySelector('ion-refresher-content');
200218
if (!refresherContent) {
@@ -209,15 +227,6 @@ export const shouldUseNativeRefresher = async (referenceEl: HTMLIonRefresherElem
209227
return (
210228
pullingSpinner !== null &&
211229
refreshingSpinner !== null &&
212-
/**
213-
* We use webkitOverflowScrolling for feature detection with rubber band scrolling
214-
* on iOS. When doing referenceEl.style, webkitOverflowScrolling is undefined on non-iOS platforms.
215-
* However, it will be the empty string on iOS.
216-
* Note that we do not use getPropertyValue (and thus need to cast as any) because calling
217-
* getPropertyValue('-webkit-overflow-scrolling') will return the empty string if it is not
218-
* set on the element, even if the platform does not support that.
219-
*/
220-
((mode === 'ios' && isPlatform('mobile') && (referenceEl.style as any).webkitOverflowScrolling !== undefined) ||
221-
mode === 'md')
230+
((mode === 'ios' && supportsRubberBandScrolling()) || mode === 'md')
222231
);
223232
};

0 commit comments

Comments
 (0)
0