Merge branch 'master' of github.com:NativeScript/NativeScript

This commit is contained in:
Martin Guillon
2021-06-09 09:51:06 +02:00
47 changed files with 1133 additions and 800 deletions

View File

@ -1,7 +1,7 @@
{
"extends": "../../.eslintrc.json",
"rules": {},
"ignorePatterns": ["!**/*", "**/node_modules/**/*", "**/__tests__/**/*"],
"ignorePatterns": ["!**/*", "**/global-types.d.ts", "**/node_modules/**/*", "**/__tests__/**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],

11
packages/core/README.md Normal file
View File

@ -0,0 +1,11 @@
<p align="center">
<a href="http://www.nativescript.org">
<img alt="NativeScript" src="https://d1lfyz5kwt8vu9.cloudfront.net/nativescript-logo-2021.png" width="100"/>
</a>
</p>
## @nativescript/core
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
[Learn more at docs.nativescript.org](https://docs.nativescript.org/)

View File

@ -92,7 +92,7 @@ function updateCurrentHelperClasses(): void {
const oldActiveFontScaleCategory = currentFontScaleCategory;
switch (fontScaleCategory) {
case FontScaleCategory.ExtraSmall: {
currentFontScaleCategory = fontScaleMediumCategoryClass;
currentFontScaleCategory = fontScaleExtraSmallCategoryClass;
break;
}
case FontScaleCategory.Medium: {

View File

@ -5,7 +5,7 @@ export const VALID_FONT_SCALES = global.isIOS // iOS supports a wider number of
export function getClosestValidFontScale(fontScale: number): number {
fontScale = Number(fontScale) || 1;
return VALID_FONT_SCALES.sort((a, b) => Math.abs(fontScale - a) - Math.abs(fontScale - b)).shift();
return VALID_FONT_SCALES.sort((a, b) => Math.abs(fontScale - a) - Math.abs(fontScale - b))[0];
}
export enum FontScaleCategory {

View File

@ -11,6 +11,7 @@ function fontScaleChanged(origFontScale: number) {
Application.notify({
eventName: Application.fontScaleChangedEvent,
object: Application,
newValue: currentFontScale,
});
}
}

View File

@ -11,6 +11,7 @@ function fontScaleChanged(origFontScale: number) {
Application.notify({
eventName: Application.fontScaleChangedEvent,
object: Application,
newValue: currentFontScale,
});
}
}

View File

@ -135,6 +135,16 @@ export interface SystemAppearanceChangedEventData extends ApplicationEventData {
newValue: 'light' | 'dark';
}
/**
* Event data containing information for font scale changed event.
*/
export interface FontScaleChangedEventData extends ApplicationEventData {
/**
* New font scale value.
*/
newValue: number;
}
/**
* Event data containing information about unhandled application errors.
*/
@ -328,6 +338,8 @@ export function on(event: 'orientationChanged', callback: (args: OrientationChan
*/
export function on(event: 'systemAppearanceChanged', callback: (args: SystemAppearanceChangedEventData) => void, thisArg?: any);
export function on(event: 'fontScaleChanged', callback: (args: FontScaleChangedEventData) => void, thisArg?: any);
/**
* Gets the orientation of the application.
* Available values: "portrait", "landscape", "unknown".

View File

@ -78,7 +78,7 @@ export class Observable {
/**
* String value used when hooking to propertyChange event.
*/
public static propertyChangeEvent: string;
static propertyChangeEvent: string;
/**
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
@ -88,6 +88,8 @@ export class Observable {
*/
on(eventNames: string, callback: (data: EventData) => void, thisArg?: any);
static on(eventName: string, callback: any, thisArg?: any): void;
/**
* Raised when a propertyChange occurs.
*/
@ -101,11 +103,15 @@ export class Observable {
*/
once(event: string, callback: (data: EventData) => void, thisArg?: any);
static once(eventName: string, callback: any, thisArg?: any): void;
/**
* Shortcut alias to the removeEventListener method.
*/
off(eventNames: string, callback?: any, thisArg?: any);
static off(eventName: string, callback?: any, thisArg?: any): void;
/**
* Adds a listener for the specified event name.
* @param eventNames Comma delimited names of the events to attach the listener to.
@ -114,6 +120,8 @@ export class Observable {
*/
addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any);
static addEventListener(eventName: string, callback: any, thisArg?: any): void;
/**
* Removes listener(s) for the specified event name.
* @param eventNames Comma delimited names of the events the specified listener is associated with.
@ -122,6 +130,8 @@ export class Observable {
*/
removeEventListener(eventNames: string, callback?: any, thisArg?: any);
static removeEventListener(eventName: string, callback?: any, thisArg?: any): void;
/**
* Updates the specified property with the provided value.
*/

View File

@ -350,31 +350,31 @@ declare class WeakRef<T> {
}
/**
* Create a Java long from a number
*/
* Create a Java long from a number
*/
declare function long(value: number): any;
/**
* Create a Java byte from a number
*/
* Create a Java byte from a number
*/
declare function byte(value: number): any;
/**
* Create a Java short from a number
*/
* Create a Java short from a number
*/
declare function short(value: number): any;
/**
* Create a Java double from a number
*/
* Create a Java double from a number
*/
declare function double(value: number): any;
/**
* Create a Java float from a number
*/
* Create a Java float from a number
*/
declare function float(value: number): any;
/**
* Create a Java char from a string
*/
* Create a Java char from a string
*/
declare function char(value: string): any;

View File

@ -1,6 +1,7 @@
import { ImageAssetBase, getRequestedImageSize } from './image-asset-common';
import { path as fsPath, knownFolders } from '../file-system';
import { ad } from '../utils';
import { Screen } from '../platform';
export * from './image-asset-common';
export class ImageAsset extends ImageAssetBase {
@ -25,66 +26,20 @@ export class ImageAsset extends ImageAssetBase {
}
public getImageAsync(callback: (image, error) => void) {
const bitmapOptions = new android.graphics.BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
// read only the file size
let bitmap = android.graphics.BitmapFactory.decodeFile(this.android, bitmapOptions);
const sourceSize = {
width: bitmapOptions.outWidth,
height: bitmapOptions.outHeight,
};
const requestedSize = getRequestedImageSize(sourceSize, this.options);
const sampleSize = org.nativescript.widgets.image.Fetcher.calculateInSampleSize(bitmapOptions.outWidth, bitmapOptions.outHeight, requestedSize.width, requestedSize.height);
const finalBitmapOptions = new android.graphics.BitmapFactory.Options();
finalBitmapOptions.inSampleSize = sampleSize;
try {
let error = null;
// read as minimum bitmap as possible (slightly bigger than the requested size)
bitmap = android.graphics.BitmapFactory.decodeFile(this.android, finalBitmapOptions);
if (bitmap) {
if (requestedSize.width !== bitmap.getWidth() || requestedSize.height !== bitmap.getHeight()) {
// scale to exact size
bitmap = android.graphics.Bitmap.createScaledBitmap(bitmap, requestedSize.width, requestedSize.height, true);
}
const rotationAngle = calculateAngleFromFile(this.android);
if (rotationAngle !== 0) {
const matrix = new android.graphics.Matrix();
matrix.postRotate(rotationAngle);
bitmap = android.graphics.Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}
if (!bitmap) {
error = "Asset '" + this.android + "' cannot be found.";
}
callback(bitmap, error);
} catch (ex) {
callback(null, ex);
}
org.nativescript.widgets.Utils.loadImageAsync(
ad.getApplicationContext(),
this.android,
JSON.stringify(this.options || {}),
Screen.mainScreen.widthPixels,
Screen.mainScreen.heightPixels,
new org.nativescript.widgets.Utils.AsyncImageCallback({
onSuccess(bitmap) {
callback(bitmap, null);
},
onError(ex) {
callback(null, ex);
},
})
);
}
}
function calculateAngleFromFile(filename: string) {
let rotationAngle = 0;
const ei = new android.media.ExifInterface(filename);
const orientation = ei.getAttributeInt(android.media.ExifInterface.TAG_ORIENTATION, android.media.ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case android.media.ExifInterface.ORIENTATION_ROTATE_90:
rotationAngle = 90;
break;
case android.media.ExifInterface.ORIENTATION_ROTATE_180:
rotationAngle = 180;
break;
case android.media.ExifInterface.ORIENTATION_ROTATE_270:
rotationAngle = 270;
break;
}
return rotationAngle;
}

View File

@ -340,7 +340,8 @@ export class ImageSource implements ImageSourceDefinition {
const dim = getScaledDimensions(size.width, size.height, maxSize);
const newSize: CGSize = CGSizeMake(dim.width, dim.height);
UIGraphicsBeginImageContextWithOptions(newSize, true, this.ios.scale);
UIGraphicsBeginImageContextWithOptions(newSize, options?.opaque ?? false, this.ios.scale);
this.ios.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height));
const resizedImage = UIGraphicsGetImageFromCurrentImageContext();

View File

@ -20,6 +20,7 @@ export declare const Application: {
lowMemoryEvent: string;
orientationChangedEvent: string;
systemAppearanceChangedEvent: string;
fontScaleChangedEvent: string;
systemAppearanceChanged: typeof systemAppearanceChanged;
getMainEntry: typeof getMainEntry;
getRootView: typeof getRootView;
@ -100,12 +101,13 @@ export type { InstrumentationMode, TimerInfo } from './profiling';
export { encoding } from './text';
export * from './trace';
export * from './ui';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString } from './utils';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, escapeRegexSymbols, convertString, dismissSoftInput, queueMacrotask } from './utils';
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback } from './utils/types';
export declare const Utils: {
GC: typeof GC;
RESOURCE_PREFIX: string;
FILE_PREFIX: string;
queueMacrotask: typeof queueMacrotask;
isFontIconURI: typeof isFontIconURI;
isDataURI: typeof isDataURI;
isFileOrResourcePath: typeof isFileOrResourcePath;
@ -143,5 +145,6 @@ export declare const Utils: {
isUndefined: typeof isUndefined;
toUIString: typeof toUIString;
verifyCallback: typeof verifyCallback;
dismissSoftInput: typeof dismissSoftInput;
};
export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -5,7 +5,7 @@ import './globals';
export { iOSApplication, AndroidApplication } from './application';
export type { ApplicationEventData, LaunchEventData, OrientationChangedEventData, UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData, AndroidActivityEventData, AndroidActivityBundleEventData, AndroidActivityRequestPermissionsEventData, AndroidActivityResultEventData, AndroidActivityNewIntentEventData, AndroidActivityBackPressedEventData, SystemAppearanceChangedEventData } from './application';
import { launchEvent, displayedEvent, uncaughtErrorEvent, discardedErrorEvent, suspendEvent, resumeEvent, exitEvent, lowMemoryEvent, orientationChangedEvent, systemAppearanceChanged, systemAppearanceChangedEvent, getMainEntry, getRootView, _resetRootView, getResources, setResources, setCssFileName, getCssFileName, loadAppCss, addCss, on, off, notify, hasListeners, run, orientation, getNativeApplication, hasLaunched, android as appAndroid, ios as iosApp, systemAppearance } from './application';
import { fontScaleChangedEvent, launchEvent, displayedEvent, uncaughtErrorEvent, discardedErrorEvent, suspendEvent, resumeEvent, exitEvent, lowMemoryEvent, orientationChangedEvent, systemAppearanceChanged, systemAppearanceChangedEvent, getMainEntry, getRootView, _resetRootView, getResources, setResources, setCssFileName, getCssFileName, loadAppCss, addCss, on, off, notify, hasListeners, run, orientation, getNativeApplication, hasLaunched, android as appAndroid, ios as iosApp, systemAppearance } from './application';
export const Application = {
launchEvent,
displayedEvent,
@ -18,6 +18,7 @@ export const Application = {
orientationChangedEvent,
systemAppearanceChangedEvent,
systemAppearanceChanged,
fontScaleChangedEvent,
getMainEntry,
getRootView,
@ -121,7 +122,7 @@ export * from './trace';
export * from './ui';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, queueMacrotask, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString } from './utils';
import { GC, isFontIconURI, isDataURI, isFileOrResourcePath, executeOnMainThread, mainThreadify, isMainThread, dispatchToMainThread, queueMacrotask, releaseNativeObject, getModuleName, openFile, openUrl, isRealDevice, layout, ad as androidUtils, iOSNativeHelper as iosUtils, Source, RESOURCE_PREFIX, FILE_PREFIX, escapeRegexSymbols, convertString, dismissSoftInput } from './utils';
import { ClassInfo, getClass, getBaseClasses, getClassInfo, isBoolean, isDefined, isFunction, isNullOrUndefined, isNumber, isObject, isString, isUndefined, toUIString, verifyCallback } from './utils/types';
export const Utils = {
@ -169,6 +170,7 @@ export const Utils = {
isUndefined,
toUIString,
verifyCallback,
dismissSoftInput,
};
export { XmlParser, ParserEventType, ParserEvent } from './xml';

View File

@ -2,8 +2,8 @@
"name": "@akylas/nativescript",
"main": "index",
"types": "index.d.ts",
"description": "NativeScript Core Modules",
"version": "8.0.3",
"description": "A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.",
"version": "8.0.7",
"homepage": "https://nativescript.org",
"repository": {
"type": "git",

View File

@ -67,8 +67,6 @@ class DeviceRef {
private _sdkVersion: string;
private _deviceType: 'Phone' | 'Tablet';
private _uuid: string;
private _language: string;
private _region: string;
get manufacturer(): string {
if (!this._manufacturer) {
@ -130,19 +128,11 @@ class DeviceRef {
}
get language(): string {
if (!this._language) {
this._language = java.util.Locale.getDefault().getLanguage().replace('_', '-');
}
return this._language;
return java.util.Locale.getDefault().getLanguage().replace('_', '-');
}
get region(): string {
if (!this._region) {
this._region = java.util.Locale.getDefault().getCountry();
}
return this._region;
return java.util.Locale.getDefault().getCountry();
}
}

View File

@ -10,8 +10,6 @@ class DeviceRef {
private _osVersion: string;
private _sdkVersion: string;
private _deviceType: 'Phone' | 'Tablet';
private _language: string;
private _region: string;
get manufacturer(): string {
return 'Apple';
@ -72,20 +70,11 @@ class DeviceRef {
}
get language(): string {
if (!this._language) {
const languages = NSLocale.preferredLanguages;
this._language = languages[0];
}
return this._language;
return NSLocale.preferredLanguages[0];
}
get region(): string {
if (!this._region) {
this._region = NSLocale.currentLocale.objectForKey(NSLocaleCountryCode);
}
return this._region;
return NSLocale.currentLocale.objectForKey(NSLocaleCountryCode);
}
}

View File

@ -353,6 +353,9 @@ export class ActionBar extends ActionBarBase {
}
private setColor(navBar: UINavigationBar, color?: Color) {
if (!navBar) {
return;
}
if (color) {
navBar.titleTextAttributes = <any>{
[NSForegroundColorAttributeName]: color.ios,
@ -443,13 +446,12 @@ export class ActionBar extends ActionBarBase {
}
private get navBar(): UINavigationBar {
const page = this.page;
// Page should be attached to frame to update the action bar.
if (!page || !page.frame) {
if (this.page?.frame?.ios?.controller) {
return (<UINavigationController>this.page.frame.ios.controller).navigationBar;
} else {
return undefined;
}
return (<UINavigationController>page.frame.ios.controller).navigationBar;
}
[colorProperty.getDefault](): UIColor {

View File

@ -224,7 +224,7 @@ export class Frame extends FrameBase {
this._ios._disableNavBarAnimation = disableNavBarAnimationCache;
}
if (this._ios.controller.navigationBar) {
if (this._ios.controller?.navigationBar) {
this._ios.controller.navigationBar.userInteractionEnabled = this.navigationQueueIsEmpty();
}
@ -666,7 +666,9 @@ class iOSFrame implements iOSFrameDefinition {
}
public set showNavigationBar(value: boolean) {
this._showNavigationBar = value;
this._controller.setNavigationBarHiddenAnimated(!value, !this._disableNavBarAnimation);
if (this._controller) {
this._controller.setNavigationBarHiddenAnimated(!value, !this._disableNavBarAnimation);
}
}
public get navBarVisibility(): 'auto' | 'never' | 'always' {

View File

@ -10,6 +10,26 @@ export class RootLayout extends RootLayoutBase {
super();
}
insertChild(view: View, atIndex: number): void {
super.insertChild(view, atIndex);
if (!view.hasGestureObservers()) {
// block tap events from going through to layers behind the view
view.nativeViewProtected.setOnTouchListener(
new android.view.View.OnTouchListener({
onTouch: function (view, event) {
return true;
},
})
);
}
}
removeChild(view: View): void {
if (view.hasGestureObservers()) {
view.nativeViewProtected.setOnTouchListener(null);
}
super.removeChild(view);
}
protected _bringToFront(view: View) {
(<android.view.View>view.nativeViewProtected).bringToFront();
}

View File

@ -177,7 +177,9 @@ class UIViewControllerImpl extends UIViewController {
}
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
frame.ios.controller.delegate = this[DELEGATE];
if (frame.ios?.controller) {
frame.ios.controller.delegate = this[DELEGATE];
}
frame._processNavigationQueue(owner);

View File

@ -40,6 +40,10 @@ export function parseCSSShadow(value: string): CSSShadow {
const first = parts[0];
const last = parts[parts.length - 1];
if (first === 'none') {
return null;
}
let colorRaw = 'black';
if (!isLength(first) && first !== 'inset') {
colorRaw = first;

View File

@ -452,6 +452,7 @@ export class CssState {
public onUnloaded(): void {
this.unsubscribeFromDynamicUpdates();
this.stopKeyframeAnimations();
}
@profile

View File

@ -446,6 +446,16 @@ export class TextBase extends TextBaseCommon {
[paddingLeftProperty.setNative](value: CoreTypes.LengthType) {
org.nativescript.widgets.ViewHelper.setPaddingLeft(this.nativeTextViewProtected, Length.toDevicePixels(value, 0) + Length.toDevicePixels(this.style.borderLeftWidth, 0));
}
[accessibilityIdentifierProperty.setNative](value: string): void {
// we override the default setter to apply it on nativeTextViewProtected
const id = Utils.ad.resources.getId(':id/nativescript_accessibility_id');
if (id) {
this.nativeTextViewProtected.setTag(id, value);
this.nativeTextViewProtected.setTag(value);
}
}
_setNativeText(reset = false): void {
if (reset) {

View File

@ -28,6 +28,10 @@ export class TextView extends TextViewBaseCommon {
this.nativeTextViewProtected.setMaxLines(value);
}
public _onReturnPress() {
this.notify({ eventName: TextView.returnPressEvent, object: this });
}
}
TextView.prototype.recycleNativeView = 'auto';

View File

@ -167,6 +167,10 @@ export class TextView extends TextViewBaseCommon {
}
}
if (replacementString === '\n') {
this.notify({ eventName: TextView.returnPressEvent, object: this });
}
if (this.formattedText) {
_updateCharactersInRangeReplacementString(this.formattedText, range.location, range.length, replacementString);
}

View File

@ -3,6 +3,7 @@ import { EditableTextBase } from '../editable-text-base';
import { Property } from '../core/properties';
export class TextViewBase extends EditableTextBase implements TextViewDefinition {
public static returnPressEvent = 'returnPress';
public maxLines: number;
}

View File

@ -1,4 +1,4 @@
import { WebViewBase, WebViewClient } from './web-view-common';
import { disableZoomProperty, WebViewBase, WebViewClient } from './web-view-common';
import { Trace } from '../../trace';
import { knownFolders } from '../../file-system';
@ -107,6 +107,7 @@ export class WebView extends WebViewBase {
const client = new WebViewClient(<any>this);
nativeView.setWebViewClient(client);
(<any>nativeView).client = client;
this._disableZoom(this.disableZoom);
}
public disposeNativeView() {
@ -119,6 +120,19 @@ export class WebView extends WebViewBase {
super.disposeNativeView();
}
private _disableZoom(value: boolean) {
if (this.nativeView && value) {
const settings = this.nativeView.getSettings();
settings.setBuiltInZoomControls(false);
settings.setSupportZoom(false);
settings.setDisplayZoomControls(false);
}
}
[disableZoomProperty.setNative](value: boolean) {
this._disableZoom(value);
}
public _loadUrl(src: string) {
const nativeView = this.nativeViewProtected;
if (!nativeView) {

View File

@ -1,5 +1,5 @@
import { WebViewNavigationType } from '.';
import { WebViewBase } from './web-view-common';
import { disableZoomProperty, WebViewBase } from './web-view-common';
import { profile } from '../../profiling';
import { Trace } from '../../trace';
export * from './web-view-common';
@ -96,9 +96,73 @@ class WKNavigationDelegateImpl extends NSObject implements WKNavigationDelegate
}
}
@NativeClass
class WKUIDelegateImpl extends NSObject implements WKUIDelegate {
public static ObjCProtocols = [WKUIDelegate];
public static initWithOwner(owner: WeakRef<WebView>): WKUIDelegateImpl {
const handler = <WKUIDelegateImpl>WKUIDelegateImpl.new();
handler._owner = owner;
return handler;
}
private _owner: WeakRef<WebView>;
webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures(webView: WKWebView, configuration: WKWebViewConfiguration, navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures): WKWebView {
if (navigationAction && (!navigationAction.targetFrame || (navigationAction.targetFrame && !navigationAction.targetFrame.mainFrame))) {
webView.loadRequest(navigationAction.request);
}
return null;
}
}
@NativeClass
@ObjCClass(UIScrollViewDelegate)
class UIScrollViewDelegateImpl extends NSObject implements UIScrollViewDelegate {
public static initWithOwner(owner: WeakRef<WebView>): UIScrollViewDelegateImpl {
const handler = <UIScrollViewDelegateImpl>UIScrollViewDelegateImpl.new();
handler._owner = owner;
return handler;
}
private _owner: WeakRef<WebView>;
private _initCurrentValues(scrollView: UIScrollView) {
const owner = this._owner.get();
if (owner && (owner._minimumZoomScale === undefined || owner._maximumZoomScale === undefined || owner._zoomScale === undefined)) {
owner._minimumZoomScale = scrollView.minimumZoomScale;
owner._maximumZoomScale = scrollView.maximumZoomScale;
owner._zoomScale = scrollView.zoomScale;
}
}
private _handleDisableZoom(scrollView: UIScrollView) {
const owner = this._owner.get();
if (owner.disableZoom) {
this._initCurrentValues(scrollView);
scrollView.maximumZoomScale = 1.0;
scrollView.minimumZoomScale = 1.0;
scrollView.zoomScale = 1.0;
}
}
scrollViewWillBeginZoomingWithView(scrollView: UIScrollView, view: UIView) {
this._handleDisableZoom(scrollView);
}
scrollViewDidZoom(scrollView) {
this._handleDisableZoom(scrollView);
}
}
export class WebView extends WebViewBase {
nativeViewProtected: WKWebView;
private _delegate: any;
private _delegate: WKNavigationDelegateImpl;
private _scrollDelegate: UIScrollViewDelegateImpl;
private _uiDelegate: WKUIDelegateImpl;
_maximumZoomScale;
_minimumZoomScale;
_zoomScale;
createNativeView() {
const jScript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'initial-scale=1.0'); document.getElementsByTagName('head')[0].appendChild(meta);";
@ -118,7 +182,11 @@ export class WebView extends WebViewBase {
initNativeView() {
super.initNativeView();
this._delegate = WKNavigationDelegateImpl.initWithOwner(new WeakRef(this));
this._scrollDelegate = UIScrollViewDelegateImpl.initWithOwner(new WeakRef(this));
this._uiDelegate = WKUIDelegateImpl.initWithOwner(new WeakRef(this));
this.ios.navigationDelegate = this._delegate;
this.ios.scrollView.delegate = this._scrollDelegate;
this.ios.UIDelegate = this._uiDelegate;
}
@profile
@ -171,4 +239,17 @@ export class WebView extends WebViewBase {
public reload() {
this.ios.reload();
}
[disableZoomProperty.setNative](value: boolean) {
if (!value && typeof this._minimumZoomScale === 'number' && typeof this._maximumZoomScale === 'number' && typeof this._zoomScale === 'number') {
if (this.ios.scrollView) {
this.ios.scrollView.minimumZoomScale = this._minimumZoomScale;
this.ios.scrollView.maximumZoomScale = this._maximumZoomScale;
this.ios.scrollView.zoomScale = this._zoomScale;
this._minimumZoomScale = undefined;
this._maximumZoomScale = undefined;
this._zoomScale = undefined;
}
}
}
}

View File

@ -3,11 +3,14 @@ import { ContainerView, CSSType } from '../core/view';
import { Property } from '../core/properties';
import { EventData } from '../../data/observable';
import { knownFolders } from '../../file-system';
import { booleanConverter } from '../core/view-base';
export * from './web-view-interfaces';
export const srcProperty = new Property<WebViewBase, string>({ name: 'src' });
export const disableZoomProperty = new Property<WebViewBase, boolean>({ name: 'disableZoom', defaultValue: false, valueConverter: booleanConverter });
@CSSType('WebView')
export abstract class WebViewBase extends ContainerView {
public static loadStartedEvent = 'loadStarted';
@ -15,6 +18,8 @@ export abstract class WebViewBase extends ContainerView {
public src: string;
public disableZoom: boolean;
public _onLoadFinished(url: string, error?: string) {
const args = <LoadEventData>{
eventName: WebViewBase.loadFinishedEvent,
@ -102,3 +107,4 @@ export interface WebViewBase {
}
srcProperty.register(WebViewBase);
disableZoomProperty.register(WebViewBase);

View File

@ -167,3 +167,7 @@ Please ensure you have your manifest correctly configured with the FileProvider.
export function isRealDevice(): boolean {
return ad.isRealDevice();
}
export function dismissSoftInput(nativeView?: any): void {
ad.dismissSoftInput(nativeView);
}

View File

@ -286,3 +286,8 @@ export function eliminateDuplicates(arr: Array<any>): Array<any>;
* Checks whether the application is running on real device and not on simulator/emulator.
*/
export function isRealDevice(): boolean;
/**
* Hides the soft input method, usually a soft keyboard.
*/
export function dismissSoftInput(nativeView?: any): void;

View File

@ -48,3 +48,10 @@ export function isRealDevice(): boolean {
}
export const ad = 0;
export function dismissSoftInput(nativeView?: UIView): void {
if (nativeView instanceof UIView && !nativeView.isFirstResponder) {
return;
}
UIApplication.sharedApplication.sendActionToFromForEvent('resignFirstResponder', null, null, null);
}