From ba01ea7ac09cc00bda1b1ff9803871298f83f92b Mon Sep 17 00:00:00 2001 From: kent Date: Tue, 13 Oct 2015 15:41:32 -0400 Subject: [PATCH 01/13] Refactoring NativePageTransitions.java class to use TransitionType enum. This will make it easier to handle different kinds of pending transitions --- src/android/NativePageTransitions.java | 117 ++++++++++++++++--------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/src/android/NativePageTransitions.java b/src/android/NativePageTransitions.java index 5d6edc1..7cd7c6d 100644 --- a/src/android/NativePageTransitions.java +++ b/src/android/NativePageTransitions.java @@ -33,6 +33,7 @@ public class NativePageTransitions extends CordovaPlugin { private float retinaFactor; private long duration; private long delay; + private TransitionType pendingTransitionType; private String drawerAction; private String drawerOrigin; private String direction; @@ -61,14 +62,9 @@ public class NativePageTransitions extends CordovaPlugin { public Object onMessage(String id, Object data) { if ("onPageFinished".equalsIgnoreCase(id)) { - if ("slide".equalsIgnoreCase(_action)) { - doSlideTransition(); - } else if ("fade".equalsIgnoreCase(_action)) { - doFadeTransition(); - } else if ("flip".equalsIgnoreCase(_action)) { - doFlipTransition(); - } else if ("drawer".equalsIgnoreCase(_action)) { - doDrawerTransition(); + TransitionType type = TransitionType.fromString(_action); + if (type != null) { + type.doTransition(this); } } return super.onMessage(id, data); @@ -123,8 +119,13 @@ public boolean execute(String action, JSONArray args, final CallbackContext call if ("executePendingTransition".equalsIgnoreCase(action)) { // TODO other transitions delay = 0; - doSlideTransition(); - return true; + if (pendingTransitionType != null) { + pendingTransitionType.doTransition(this); + pendingTransitionType = null; + return true; + } else { + return false; + } } final JSONObject json = args.getJSONObject(0); @@ -185,15 +186,12 @@ public void run() { } } - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else if (delay > -1) { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doSlideTransition(); - } - } else if (delay > -1) { - doSlideTransition(); + } else { + pendingTransitionType = TransitionType.SLIDE; } } }); @@ -240,15 +238,12 @@ public void run() { bringToFront(imageView2); } - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doDrawerTransition(); - } } else { - doDrawerTransition(); + pendingTransitionType = TransitionType.DRAWER; } } }); @@ -264,15 +259,12 @@ public void run() { imageView.setImageBitmap(getBitmap()); bringToFront(imageView); - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doFadeTransition(); - } } else { - doFadeTransition(); + pendingTransitionType = TransitionType.FADE; } } }); @@ -287,15 +279,12 @@ public void run() { @Override public void run() { imageView.setImageBitmap(getBitmap()); - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doFlipTransition(); - } } else { - doFlipTransition(); + pendingTransitionType = TransitionType.FLIP; } } }); @@ -303,7 +292,7 @@ public void run() { return true; } - private void doFadeTransition() { + public void doFadeTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -366,7 +355,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doFlipTransition() { + public void doFlipTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -436,7 +425,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doSlideTransition() { + public void doSlideTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -571,7 +560,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doDrawerTransition() { + public void doDrawerTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -713,4 +702,54 @@ private void enableHardwareAcceleration() { } } } + + private boolean shouldFireNow() { + return delay >= 0; + } + + private enum TransitionType { + FLIP("flip") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doFlipTransition(); + } + }, + SLIDE("slide") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doSlideTransition(); + } + }, + FADE("fade") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doFadeTransition(); + } + }, + DRAWER("drawer") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doDrawerTransition(); + } + }; + + private String code; + + private TransitionType(String code) { + this.code = code; + } + + public abstract void doTransition(NativePageTransitions plugin); + + public static TransitionType fromString(String text) { + if (text != null) { + for (TransitionType type : TransitionType.values()) { + if (text.equalsIgnoreCase(type.code)) { + return type; + } + } + } + return null; + } + } } \ No newline at end of file From 8fc1c339d4358064361301a0b3ea32a6634633a6 Mon Sep 17 00:00:00 2001 From: kent Date: Tue, 13 Oct 2015 17:10:56 -0400 Subject: [PATCH 02/13] #62 - added function option for delay instead of integer to control when animating pending transition happens --- www/NativePageTransitions.js | 97 +++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index 33ac670..f56cd05 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -4,16 +4,14 @@ function NativePageTransitions() { NativePageTransitions.prototype.globalOptions = { duration: 400, iosdelay: 60, // a number of milliseconds, or -1 (call executePendingTransition() when ready) - androiddelay: 70, // a number of milliseconds, or -1 (call executePendingTransition() when ready) + androiddelay: 70, // a number of milliseconds, a function that takes an animate callback that will execute pending transition when invoked, or -1 to manually call executePendingTransition() when ready winphonedelay: 200, slowdownfactor: 4, fixedPixelsTop: 0, // currently for slide left/right only fixedPixelsBottom: 0 // currently for slide left/right only }; -NativePageTransitions.prototype.executePendingTransition = function (onSuccess, onError) { - cordova.exec(onSuccess, onError, "NativePageTransitions", "executePendingTransition", []); -}; +NativePageTransitions.prototype.executePendingTransition = executePendingTransition; NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { var opts = options || {}; @@ -24,15 +22,7 @@ NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); if (opts.fixedPixelsTop == undefined || opts.fixedPixelsTop == "null") { opts.fixedPixelsTop = this.globalOptions.fixedPixelsTop; } @@ -42,6 +32,7 @@ NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { // setting slowdownfactor > 1 makes the next page slide less pixels. Use 1 for side-by-side. opts.slowdownfactor = opts.slowdownfactor || this.globalOptions.slowdownfactor; cordova.exec(onSuccess, onError, "NativePageTransitions", "slide", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.drawer = function (options, onSuccess, onError) { @@ -54,16 +45,9 @@ NativePageTransitions.prototype.drawer = function (options, onSuccess, onError) if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); cordova.exec(onSuccess, onError, "NativePageTransitions", "drawer", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.flip = function (options, onSuccess, onError) { @@ -75,16 +59,9 @@ NativePageTransitions.prototype.flip = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); cordova.exec(onSuccess, onError, "NativePageTransitions", "flip", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.curl = function (options, onSuccess, onError) { @@ -96,10 +73,9 @@ NativePageTransitions.prototype.curl = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } + var delayCallbacks = setupDelayOptions(opts, {android: false, winphone: false}); cordova.exec(onSuccess, onError, "NativePageTransitions", "curl", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.fade = function (options, onSuccess, onError) { @@ -110,13 +86,9 @@ NativePageTransitions.prototype.fade = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } + var delayCallbacks = setupDelayOptions(opts, {winphone: false}); cordova.exec(onSuccess, onError, "NativePageTransitions", "fade", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype._validateHref = function (href, errCallback) { @@ -173,4 +145,49 @@ NativePageTransitions.install = function () { return window.plugins.nativepagetransitions; }; +function setupDelayOptions(opts, whichOS) { + whichOS = whichOS || {}; + whichOS = { + android: whichOS.android !== false, + ios: whichOS.ios !== false, + winphone: whichOS.winphone !== false + }; + var callbacks = []; + var globalOptions = NativePageTransitions.prototype.globalOptions; + if (whichOS.android) { + if (opts.androiddelay == undefined || opts.androiddelay == "null") { + opts.androiddelay = globalOptions.androiddelay; + } else if (typeof opts.androiddelay == 'function') { + callbacks.push(opts.androiddelay); + opts.androiddelay = -1; + } + } + if (whichOS.ios && (opts.iosdelay == undefined || opts.iosdelay == "null")) { + opts.iosdelay = globalOptions.iosdelay; + } + if (whichOS.winphone && (opts.winphonedelay == undefined || opts.winphonedelay == "null")) { + opts.winphonedelay = globalOptions.winphonedelay; + } + return callbacks; +} + +function animate(onSuccess, onError) { + onSuccess = onSuccess || function() {}; + onError = onError || function() {}; + executePendingTransition(onSuccess, onError); +} + +function fireDelayCallbacks(callbacks) { + if (callbacks) { + for (var i = 0; i < callbacks.length; i++) { + var callback = callbacks[i]; + callback(animate); + } + } +} + +function executePendingTransition(onSuccess, onError) { + cordova.exec(onSuccess, onError, "NativePageTransitions", "executePendingTransition", []); +}; + cordova.addConstructor(NativePageTransitions.install); \ No newline at end of file From e6bb659ea21a389d4b0d7685735e213c46f09888 Mon Sep 17 00:00:00 2001 From: kent Date: Fri, 16 Oct 2015 14:54:39 -0400 Subject: [PATCH 03/13] Added ios delay capability to be a function --- www/NativePageTransitions.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index f56cd05..aba54c6 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -162,8 +162,13 @@ function setupDelayOptions(opts, whichOS) { opts.androiddelay = -1; } } - if (whichOS.ios && (opts.iosdelay == undefined || opts.iosdelay == "null")) { - opts.iosdelay = globalOptions.iosdelay; + if (whichOS.ios) { + if (opts.iosdelay == undefined || opts.iosdelay == "null")) { + opts.iosdelay = globalOptions.iosdelay; + } else if (typeof opts.iosdelay == 'function') { + callbacks.push(opts.iosdelay); + opts.iosdelay = -1; + } } if (whichOS.winphone && (opts.winphonedelay == undefined || opts.winphonedelay == "null")) { opts.winphonedelay = globalOptions.winphonedelay; From 70a0e46a6f53c8dd8eb4a1c16c12b859a0639fae Mon Sep 17 00:00:00 2001 From: kent Date: Fri, 16 Oct 2015 15:04:20 -0400 Subject: [PATCH 04/13] Remove extra paren --- www/NativePageTransitions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index aba54c6..fd24140 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -163,7 +163,7 @@ function setupDelayOptions(opts, whichOS) { } } if (whichOS.ios) { - if (opts.iosdelay == undefined || opts.iosdelay == "null")) { + if (opts.iosdelay == undefined || opts.iosdelay == "null") { opts.iosdelay = globalOptions.iosdelay; } else if (typeof opts.iosdelay == 'function') { callbacks.push(opts.iosdelay); From 55c1a29b5971e0aef25eae7db8fe162933218030 Mon Sep 17 00:00:00 2001 From: EddyVerbruggen Date: Tue, 20 Oct 2015 10:47:38 +0200 Subject: [PATCH 05/13] #62 An alternative to configuring delay values. Added iOS flip support. --- plugin.xml | 2 +- src/ios/NativePageTransitions.m | 190 ++++++++++++++++++-------------- 2 files changed, 109 insertions(+), 83 deletions(-) diff --git a/plugin.xml b/plugin.xml index 736214b..ff8ba45 100755 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ + version="0.4.4-dev"> Native Page Transitions diff --git a/src/ios/NativePageTransitions.m b/src/ios/NativePageTransitions.m index 745b9d4..edffe9f 100755 --- a/src/ios/NativePageTransitions.m +++ b/src/ios/NativePageTransitions.m @@ -41,10 +41,8 @@ - (void)dispose - (void) executePendingTransition:(CDVInvokedUrlCommand*)command { if (_slideOptions != nil) { [self performSlideTransition]; - - // TODO other transitions -// } else if (_flipOptions != nil) { -// [self performFlipTransition]; + } else if (_flipOptions != nil) { + [self performFlipTransition]; } } @@ -182,6 +180,14 @@ - (void) performSlideTransition { CGFloat width = self.viewController.view.frame.size.width; CGFloat height = self.viewController.view.frame.size.height; + // correct landscape detection on iOS < 8 + BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); + if (isLandscape && width < height) { + CGFloat temp = width; + width = height; + height = temp; + } + if ([direction isEqualToString:@"left"]) { transitionToX = -width; screenshotSlowdownFactor = [slowdownfactor intValue]; @@ -251,6 +257,104 @@ - (void) performSlideTransition { } } +- (void) flip:(CDVInvokedUrlCommand*)command { + _command = command; + NSMutableDictionary *args = [command.arguments objectAtIndex:0]; + + // overlay the webview with a screenshot to prevent the user from seeing changes in the webview before the flip kicks in + CGSize viewSize = self.viewController.view.bounds.size; + + UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); + [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; + + // Read the UIImage object + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + CGFloat width = self.viewController.view.frame.size.width; + CGFloat height = self.viewController.view.frame.size.height; + [_screenShotImageView setFrame:CGRectMake(0, 0, width, height)]; + + _screenShotImageView = [[UIImageView alloc]initWithFrame:[self.viewController.view.window frame]]; + [_screenShotImageView setImage:image]; + [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; + + if ([self loadHrefIfPassed:[args objectForKey:@"href"]]) { + // pass in -1 for manual (requires you to call executePendingTransition) + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + _flipOptions = args; + if (delay < 0) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } else { + [self performFlipTransition]; + } + } +} + +- (void) performFlipTransition { + NSMutableDictionary *args = _flipOptions; + _flipOptions = nil; + NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; + NSString *direction = [args objectForKey:@"direction"]; + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + if (delay < 0) { + delay = 0; + } + // duration is passed in ms, but needs to be in sec here + duration = duration / 1000; + + CGFloat width = self.viewController.view.frame.size.width; + CGFloat height = self.viewController.view.frame.size.height; + + UIViewAnimationOptions animationOptions; + if ([direction isEqualToString:@"right"]) { + if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { + animationOptions = UIViewAnimationOptionTransitionFlipFromTop; + } else { + animationOptions = UIViewAnimationOptionTransitionFlipFromLeft; + } + } else if ([direction isEqualToString:@"left"]) { + if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { + animationOptions = UIViewAnimationOptionTransitionFlipFromBottom; + } else { + animationOptions = UIViewAnimationOptionTransitionFlipFromRight; + } + } else if ([direction isEqualToString:@"up"]) { + if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { + animationOptions = UIViewAnimationOptionTransitionFlipFromRight; + } else { + animationOptions = UIViewAnimationOptionTransitionFlipFromTop; + } + } else if ([direction isEqualToString:@"down"]) { + if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { + animationOptions = UIViewAnimationOptionTransitionFlipFromLeft; + } else { + animationOptions = UIViewAnimationOptionTransitionFlipFromBottom; + } + } else { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"direction should be one of up|down|left|right"]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + return; + } + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + // remove the screenshot halfway during the transition + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [_screenShotImageView removeFromSuperview]; + }); + [UIView transitionWithView:self.viewController.view + duration:duration + options:animationOptions | UIViewAnimationOptionAllowAnimatedContent + animations:^{} + completion:^(BOOL finished) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + }); +} + - (void) drawer:(CDVInvokedUrlCommand*)command { _command = command; NSMutableDictionary *args = [command.arguments objectAtIndex:0]; @@ -370,84 +474,6 @@ - (void) drawer:(CDVInvokedUrlCommand*)command { } } -- (void) flip:(CDVInvokedUrlCommand*)command { - _command = command; - NSMutableDictionary *args = [command.arguments objectAtIndex:0]; - NSString *direction = [args objectForKey:@"direction"]; - NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; - NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; - NSString *href = [args objectForKey:@"href"]; - - // duration is passed in ms, but needs to be in sec here - duration = duration / 1000; - - // overlay the webview with a screenshot to prevent the user from seeing changes in the webview before the flip kicks in - CGSize viewSize = self.viewController.view.bounds.size; - - UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); - [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; - - // Read the UIImage object - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - CGFloat width = self.viewController.view.frame.size.width; - CGFloat height = self.viewController.view.frame.size.height; - [_screenShotImageView setFrame:CGRectMake(0, 0, width, height)]; - - _screenShotImageView = [[UIImageView alloc]initWithFrame:[self.viewController.view.window frame]]; - [_screenShotImageView setImage:image]; - [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; - - UIViewAnimationOptions animationOptions; - if ([direction isEqualToString:@"right"]) { - if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { - animationOptions = UIViewAnimationOptionTransitionFlipFromTop; - } else { - animationOptions = UIViewAnimationOptionTransitionFlipFromLeft; - } - } else if ([direction isEqualToString:@"left"]) { - if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { - animationOptions = UIViewAnimationOptionTransitionFlipFromBottom; - } else { - animationOptions = UIViewAnimationOptionTransitionFlipFromRight; - } - } else if ([direction isEqualToString:@"up"]) { - if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { - animationOptions = UIViewAnimationOptionTransitionFlipFromRight; - } else { - animationOptions = UIViewAnimationOptionTransitionFlipFromTop; - } - } else if ([direction isEqualToString:@"down"]) { - if (width < height && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { - animationOptions = UIViewAnimationOptionTransitionFlipFromLeft; - } else { - animationOptions = UIViewAnimationOptionTransitionFlipFromBottom; - } - } else { - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"direction should be one of up|down|left|right"]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - return; - } - - if ([self loadHrefIfPassed:href]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - // remove the screenshot halfway during the transition - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_screenShotImageView removeFromSuperview]; - }); - [UIView transitionWithView:self.viewController.view - duration:duration - options:animationOptions | UIViewAnimationOptionAllowAnimatedContent - animations:^{} - completion:^(BOOL finished) { - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; - }); - } -} - - (void) curl:(CDVInvokedUrlCommand*)command { _command = command; NSMutableDictionary *args = [command.arguments objectAtIndex:0]; From 5b8d9fe655f5dc31e836857c2a717092c008d692 Mon Sep 17 00:00:00 2001 From: EddyVerbruggen Date: Tue, 20 Oct 2015 11:55:31 +0200 Subject: [PATCH 06/13] #62 An alternative to configuring delay values. Added Android flip support. --- src/android/NativePageTransitions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/android/NativePageTransitions.java b/src/android/NativePageTransitions.java index 7cd7c6d..b924e1d 100644 --- a/src/android/NativePageTransitions.java +++ b/src/android/NativePageTransitions.java @@ -113,11 +113,9 @@ public void initialize(CordovaInterface cordova, CordovaWebView webView) { @Override public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException { - _action = action; _callbackContext = callbackContext; if ("executePendingTransition".equalsIgnoreCase(action)) { - // TODO other transitions delay = 0; if (pendingTransitionType != null) { pendingTransitionType.doTransition(this); @@ -128,6 +126,8 @@ public boolean execute(String action, JSONArray args, final CallbackContext call } } + _action = action; + final JSONObject json = args.getJSONObject(0); final String href = json.isNull("href") ? null : json.getString("href"); @@ -697,7 +697,7 @@ private void enableHardwareAcceleration() { WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (BEFORE_KITKAT && !isCrosswalk) { + if (BEFORE_KITKAT) { getView().setLayerType(View.LAYER_TYPE_SOFTWARE, null); } } From 84e1c96ab908d21d73198f21d51618e717aebc39 Mon Sep 17 00:00:00 2001 From: EddyVerbruggen Date: Tue, 20 Oct 2015 12:02:35 +0200 Subject: [PATCH 07/13] #62 An alternative to configuring delay values. Added Android fade and drawer support. --- src/android/NativePageTransitions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/android/NativePageTransitions.java b/src/android/NativePageTransitions.java index b924e1d..08e9e8b 100644 --- a/src/android/NativePageTransitions.java +++ b/src/android/NativePageTransitions.java @@ -56,7 +56,7 @@ public class NativePageTransitions extends CordovaPlugin { try { Class.forName("org.crosswalk.engine.XWalkWebViewEngine"); isCrosswalk = true; - } catch (Exception e) { + } catch (Exception ignore) { } } @@ -657,7 +657,7 @@ private Bitmap getBitmap() { try { TextureView textureView = findCrosswalkTextureView((ViewGroup) getView()); bitmap = textureView.getBitmap(); - } catch(Exception e) { + } catch(Exception ignore) { } } else { View view = getView(); From 85a3091356af8508250d987d43d1f29a4e492335 Mon Sep 17 00:00:00 2001 From: EddyVerbruggen Date: Tue, 20 Oct 2015 15:27:08 +0200 Subject: [PATCH 08/13] #62 An alternative to configuring delay values. Added iOS fade and drawer support. --- plugin.xml | 2 +- src/ios/NativePageTransitions.h | 2 + src/ios/NativePageTransitions.m | 382 ++++++++++++++++++-------------- 3 files changed, 220 insertions(+), 166 deletions(-) diff --git a/plugin.xml b/plugin.xml index ff8ba45..89dcb2e 100755 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ + version="0.5.0"> Native Page Transitions diff --git a/src/ios/NativePageTransitions.h b/src/ios/NativePageTransitions.h index a0732cc..e1b7099 100755 --- a/src/ios/NativePageTransitions.h +++ b/src/ios/NativePageTransitions.h @@ -5,6 +5,8 @@ @property (retain) NSMutableDictionary *slideOptions; @property (retain) NSMutableDictionary *flipOptions; +@property (retain) NSMutableDictionary *drawerOptions; +@property (retain) NSMutableDictionary *fadeOptions; @property (strong, nonatomic) IBOutlet UIImageView *screenShotImageViewTop; @property (strong, nonatomic) IBOutlet UIImageView *screenShotImageViewBottom; diff --git a/src/ios/NativePageTransitions.m b/src/ios/NativePageTransitions.m index edffe9f..d118c90 100755 --- a/src/ios/NativePageTransitions.m +++ b/src/ios/NativePageTransitions.m @@ -43,6 +43,10 @@ - (void) executePendingTransition:(CDVInvokedUrlCommand*)command { [self performSlideTransition]; } else if (_flipOptions != nil) { [self performFlipTransition]; + } else if (_drawerOptions != nil) { + [self performDrawerTransition]; + } else if (_fadeOptions != nil) { + [self performFadeTransition]; } } @@ -205,56 +209,55 @@ - (void) performSlideTransition { webviewFromY = (-height/webviewSlowdownFactor)+_nonWebViewHeight; } + [UIView animateWithDuration:duration + delay:delay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [_screenShotImageView setFrame:CGRectMake(transitionToX/screenshotSlowdownFactor, transitionToY, width, height)]; + } + completion:^(BOOL finished) { + [_screenShotImageView removeFromSuperview]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + // also, fade out the screenshot a bit to give it some depth + if ([slowdownfactor intValue] != 1 && ([direction isEqualToString:@"left"] || [direction isEqualToString:@"up"])) { [UIView animateWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{ - [_screenShotImageView setFrame:CGRectMake(transitionToX/screenshotSlowdownFactor, transitionToY, width, height)]; + _screenShotImageView.alpha = lowerLayerAlpha; } completion:^(BOOL finished) { - [_screenShotImageView removeFromSuperview]; - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; }]; - - // also, fade out the screenshot a bit to give it some depth - if ([slowdownfactor intValue] != 1 && ([direction isEqualToString:@"left"] || [direction isEqualToString:@"up"])) { - [UIView animateWithDuration:duration - delay:delay - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - _screenShotImageView.alpha = lowerLayerAlpha; - } - completion:^(BOOL finished) { - }]; - } - - [self.transitionView setFrame:CGRectMake(-transitionToX/webviewSlowdownFactor, webviewFromY, width, height-_nonWebViewHeight)]; - + } + + [self.transitionView setFrame:CGRectMake(-transitionToX/webviewSlowdownFactor, webviewFromY, width, height-_nonWebViewHeight)]; + + [UIView animateWithDuration:duration + delay:delay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.transitionView setFrame:CGRectMake(0, webviewToY, width, height-_nonWebViewHeight)]; + } + completion:^(BOOL finished) { + // doesn't matter if these weren't added + [_screenShotImageViewTop removeFromSuperview]; + [_screenShotImageViewBottom removeFromSuperview]; + }]; + + if ([slowdownfactor intValue] != 1 && ([direction isEqualToString:@"right"] || [direction isEqualToString:@"down"])) { + self.transitionView.alpha = lowerLayerAlpha; [UIView animateWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseInOut animations:^{ - [self.transitionView setFrame:CGRectMake(0, webviewToY, width, height-_nonWebViewHeight)]; + self.transitionView.alpha = 1.0; } completion:^(BOOL finished) { - // doesn't matter if these weren't added - [_screenShotImageViewTop removeFromSuperview]; - [_screenShotImageViewBottom removeFromSuperview]; }]; - - if ([slowdownfactor intValue] != 1 && ([direction isEqualToString:@"right"] || [direction isEqualToString:@"down"])) { - self.transitionView.alpha = lowerLayerAlpha; - [UIView animateWithDuration:duration - delay:delay - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - self.transitionView.alpha = 1.0; - } - completion:^(BOOL finished) { - }]; - } + } } - (void) flip:(CDVInvokedUrlCommand*)command { @@ -339,34 +342,30 @@ - (void) performFlipTransition { return; } - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - // remove the screenshot halfway during the transition - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_screenShotImageView removeFromSuperview]; - }); - [UIView transitionWithView:self.viewController.view - duration:duration - options:animationOptions | UIViewAnimationOptionAllowAnimatedContent - animations:^{} - completion:^(BOOL finished) { - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; - }]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + // remove the screenshot halfway during the transition + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [_screenShotImageView removeFromSuperview]; }); + [UIView transitionWithView:self.viewController.view + duration:duration + options:animationOptions | UIViewAnimationOptionAllowAnimatedContent + animations:^{} + completion:^(BOOL finished) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + }); } - (void) drawer:(CDVInvokedUrlCommand*)command { _command = command; NSMutableDictionary *args = [command.arguments objectAtIndex:0]; NSString *action = [args objectForKey:@"action"]; - NSString *origin = [args objectForKey:@"origin"]; NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; - NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; - NSString *href = [args objectForKey:@"href"]; - // duration/delay is passed in ms, but needs to be in sec here + // duration is passed in ms, but needs to be in sec here duration = duration / 1000; - delay = delay / 1000; CGFloat width = self.viewController.view.frame.size.width; CGFloat height = self.viewController.view.frame.size.height; @@ -381,26 +380,6 @@ - (void) drawer:(CDVInvokedUrlCommand*)command { height = temp; } - CGFloat transitionToX = 0; - CGFloat webviewTransitionFromX = 0; - int screenshotPx = 44; - - if ([action isEqualToString:@"open"]) { - if ([origin isEqualToString:@"right"]) { - transitionToX = -width+screenshotPx; - } else { - transitionToX = width-screenshotPx; - } - } else if ([action isEqualToString:@"close"]) { - if ([origin isEqualToString:@"right"]) { - transitionToX = screenshotPx; - webviewTransitionFromX = -width+screenshotPx; - } else { - transitionToX = -width+screenshotPx; - webviewTransitionFromX = width-screenshotPx; - } - } - CGSize viewSize = self.viewController.view.bounds.size; UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; @@ -432,48 +411,170 @@ - (void) drawer:(CDVInvokedUrlCommand*)command { self.transitionView.layer.shadowOpacity = 0.5f; self.transitionView.layer.shadowPath = shadowPath.CGPath; } + + if ([self loadHrefIfPassed:[args objectForKey:@"href"]]) { + // pass in -1 for manual (requires you to call executePendingTransition) + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + _drawerOptions = args; + if (delay < 0) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + [self performDrawerTransition]; + } + } +} + +- (void) performDrawerTransition { + NSMutableDictionary *args = _drawerOptions; + _drawerOptions = nil; + NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; + NSString *action = [args objectForKey:@"action"]; + NSString *origin = [args objectForKey:@"origin"]; + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + if (delay < 0) { + delay = 0; + } + // duration is passed in ms, but needs to be in sec here + duration = duration / 1000; + delay = delay / 1000; + + CGFloat width = self.viewController.view.frame.size.width; + CGFloat height = self.viewController.view.frame.size.height; - if ([self loadHrefIfPassed:href]) { - if ([action isEqualToString:@"open"]) { - [UIView animateWithDuration:duration - delay:delay - options:UIViewAnimationOptionCurveEaseInOut // TODO: allow passing in? - animations:^{ - [_screenShotImageView setFrame:CGRectMake(transitionToX, 0, width, height)]; - } - completion:^(BOOL finished) { - if ([action isEqualToString:@"close"]) { - _screenShotImageView = nil; - // [_screenShotImageView removeFromSuperview]; - } - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; + // correct landscape detection on iOS < 8 + BOOL isLandscape = UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation); + if (isLandscape && width < height) { + CGFloat temp = width; + width = height; + height = temp; + } + + CGFloat transitionToX = 0; + CGFloat webviewTransitionFromX = 0; + int screenshotPx = 44; + + if ([action isEqualToString:@"open"]) { + if ([origin isEqualToString:@"right"]) { + transitionToX = -width+screenshotPx; + } else { + transitionToX = width-screenshotPx; } - - if ([action isEqualToString:@"close"]) { - [self.transitionView setFrame:CGRectMake(webviewTransitionFromX, _nonWebViewHeight, width, height-_nonWebViewHeight)]; - - // position the webview above the screenshot just after the animation kicks in so no flash of the webview occurs - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay+50 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [self.transitionView.superview bringSubviewToFront:self.transitionView]; - }); - - [UIView animateWithDuration:duration - delay:delay - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self.transitionView setFrame:CGRectMake(0, _nonWebViewHeight, width, height-_nonWebViewHeight)]; + } else if ([action isEqualToString:@"close"]) { + if ([origin isEqualToString:@"right"]) { + transitionToX = screenshotPx; + webviewTransitionFromX = -width+screenshotPx; + } else { + transitionToX = -width+screenshotPx; + webviewTransitionFromX = width-screenshotPx; + } + } + + if ([action isEqualToString:@"open"]) { + [UIView animateWithDuration:duration + delay:delay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [_screenShotImageView setFrame:CGRectMake(transitionToX, 0, width, height)]; + } + completion:^(BOOL finished) { + if ([action isEqualToString:@"close"]) { + _screenShotImageView = nil; + // [_screenShotImageView removeFromSuperview]; } - completion:^(BOOL finished) { - [_screenShotImageView removeFromSuperview]; - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + } + + if ([action isEqualToString:@"close"]) { + [self.transitionView setFrame:CGRectMake(webviewTransitionFromX, _nonWebViewHeight, width, height-_nonWebViewHeight)]; + + // position the webview above the screenshot just after the animation kicks in so no flash of the webview occurs + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay+50 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [self.transitionView.superview bringSubviewToFront:self.transitionView]; + }); + + [UIView animateWithDuration:duration + delay:delay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.transitionView setFrame:CGRectMake(0, _nonWebViewHeight, width, height-_nonWebViewHeight)]; + } + completion:^(BOOL finished) { + [_screenShotImageView removeFromSuperview]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + } +} + +- (void) fade:(CDVInvokedUrlCommand*)command { + _command = command; + NSMutableDictionary *args = [command.arguments objectAtIndex:0]; + + // overlay the webview with a screenshot to prevent the user from seeing changes in the webview before the fade kicks in + CGSize viewSize = self.viewController.view.bounds.size; + + UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); + [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; + + // Read the UIImage object + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + CGFloat width = self.viewController.view.frame.size.width; + CGFloat height = self.viewController.view.frame.size.height; + [_screenShotImageView setFrame:CGRectMake(0, 0, width, height)]; + + _screenShotImageView = [[UIImageView alloc]initWithFrame:[self.viewController.view.window frame]]; + [_screenShotImageView setImage:image]; + [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; + + if ([self loadHrefIfPassed:[args objectForKey:@"href"]]) { + // pass in -1 for manual (requires you to call executePendingTransition) + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + _fadeOptions = args; + if (delay < 0) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + [self performFadeTransition]; } } } +- (void) performFadeTransition { + NSMutableDictionary *args = _fadeOptions; + _fadeOptions = nil; + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; + + if (delay < 0) { + delay = 0; + } + // delay/duration is passed in ms, but needs to be in sec here + duration = duration / 1000; + delay = delay / 1000; + + UIViewAnimationOptions animationOptions = UIViewAnimationOptionTransitionCrossDissolve; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + // remove the screenshot halfway during the transition + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [_screenShotImageView removeFromSuperview]; + }); + [UIView transitionWithView:self.viewController.view + duration:duration + options:animationOptions | UIViewAnimationOptionAllowAnimatedContent + animations:^{} + completion:^(BOOL finished) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + }); +} + - (void) curl:(CDVInvokedUrlCommand*)command { _command = command; NSMutableDictionary *args = [command.arguments objectAtIndex:0]; @@ -481,28 +582,28 @@ - (void) curl:(CDVInvokedUrlCommand*)command { NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; NSString *href = [args objectForKey:@"href"]; - + // duration is passed in ms, but needs to be in sec here duration = duration / 1000; - + // overlay the webview with a screenshot to prevent the user from seeing changes in the webview before the flip kicks in CGSize viewSize = self.viewController.view.bounds.size; - + UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; - + // Read the UIImage object UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); - + CGFloat width = self.viewController.view.frame.size.width; CGFloat height = self.viewController.view.frame.size.height; [_screenShotImageView setFrame:CGRectMake(0, 0, width, height)]; - + _screenShotImageView = [[UIImageView alloc]initWithFrame:[self.viewController.view.window frame]]; [_screenShotImageView setImage:image]; [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; - + UIViewAnimationOptions animationOptions; if ([direction isEqualToString:@"up"]) { animationOptions = UIViewAnimationOptionTransitionCurlUp; @@ -513,7 +614,7 @@ - (void) curl:(CDVInvokedUrlCommand*)command { [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; return; } - + if ([self loadHrefIfPassed:href]) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ // remove the screenshot halfway during the transition @@ -532,55 +633,6 @@ - (void) curl:(CDVInvokedUrlCommand*)command { } } -- (void) fade:(CDVInvokedUrlCommand*)command { - _command = command; - NSMutableDictionary *args = [command.arguments objectAtIndex:0]; - NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; - NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; - NSString *href = [args objectForKey:@"href"]; - - // duration is passed in ms, but needs to be in sec here - duration = duration / 1000; - - // overlay the webview with a screenshot to prevent the user from seeing changes in the webview before the fade kicks in - CGSize viewSize = self.viewController.view.bounds.size; - - UIGraphicsBeginImageContextWithOptions(viewSize, YES, 0.0); - [self.viewController.view.layer renderInContext:UIGraphicsGetCurrentContext()]; - - // Read the UIImage object - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - CGFloat width = self.viewController.view.frame.size.width; - CGFloat height = self.viewController.view.frame.size.height; - [_screenShotImageView setFrame:CGRectMake(0, 0, width, height)]; - - _screenShotImageView = [[UIImageView alloc]initWithFrame:[self.viewController.view.window frame]]; - [_screenShotImageView setImage:image]; - [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; - - UIViewAnimationOptions animationOptions; - animationOptions = UIViewAnimationOptionTransitionCrossDissolve; - - if ([self loadHrefIfPassed:href]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - // remove the screenshot halfway during the transition - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_screenShotImageView removeFromSuperview]; - }); - [UIView transitionWithView:self.viewController.view - duration:duration - options:animationOptions | UIViewAnimationOptionAllowAnimatedContent - animations:^{} - completion:^(BOOL finished) { - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; - }); - } -} - - (BOOL) loadHrefIfPassed:(NSString*) href { if (href != nil && ![href isEqual:[NSNull null]]) { if (![href hasPrefix:@"#"] && [href rangeOfString:@".html"].location != NSNotFound) { From 3afca427470c4272874d073abeecc9acd47c70fa Mon Sep 17 00:00:00 2001 From: EddyVerbruggen Date: Tue, 20 Oct 2015 15:33:58 +0200 Subject: [PATCH 09/13] #62 An alternative to configuring delay values. Added iOS curl support. --- src/ios/NativePageTransitions.h | 1 + src/ios/NativePageTransitions.m | 62 +++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/ios/NativePageTransitions.h b/src/ios/NativePageTransitions.h index e1b7099..90585df 100755 --- a/src/ios/NativePageTransitions.h +++ b/src/ios/NativePageTransitions.h @@ -7,6 +7,7 @@ @property (retain) NSMutableDictionary *flipOptions; @property (retain) NSMutableDictionary *drawerOptions; @property (retain) NSMutableDictionary *fadeOptions; +@property (retain) NSMutableDictionary *curlOptions; @property (strong, nonatomic) IBOutlet UIImageView *screenShotImageViewTop; @property (strong, nonatomic) IBOutlet UIImageView *screenShotImageViewBottom; diff --git a/src/ios/NativePageTransitions.m b/src/ios/NativePageTransitions.m index d118c90..0787869 100755 --- a/src/ios/NativePageTransitions.m +++ b/src/ios/NativePageTransitions.m @@ -47,6 +47,8 @@ - (void) executePendingTransition:(CDVInvokedUrlCommand*)command { [self performDrawerTransition]; } else if (_fadeOptions != nil) { [self performFadeTransition]; + } else if (_curlOptions != nil) { + [self performCurlTransition]; } } @@ -578,10 +580,7 @@ - (void) performFadeTransition { - (void) curl:(CDVInvokedUrlCommand*)command { _command = command; NSMutableDictionary *args = [command.arguments objectAtIndex:0]; - NSString *direction = [args objectForKey:@"direction"]; NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; - NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; - NSString *href = [args objectForKey:@"href"]; // duration is passed in ms, but needs to be in sec here duration = duration / 1000; @@ -604,6 +603,33 @@ - (void) curl:(CDVInvokedUrlCommand*)command { [_screenShotImageView setImage:image]; [self.transitionView.superview insertSubview:_screenShotImageView aboveSubview:self.transitionView]; + if ([self loadHrefIfPassed:[args objectForKey:@"href"]]) { + // pass in -1 for manual (requires you to call executePendingTransition) + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + _curlOptions = args; + if (delay < 0) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + } else { + [self performCurlTransition]; + } + } +} + +- (void) performCurlTransition { + NSMutableDictionary *args = _curlOptions; + _curlOptions = nil; + NSTimeInterval delay = [[args objectForKey:@"iosdelay"] doubleValue]; + NSTimeInterval duration = [[args objectForKey:@"duration"] doubleValue]; + NSString *direction = [args objectForKey:@"direction"]; + + if (delay < 0) { + delay = 0; + } + // delay/duration is passed in ms, but needs to be in sec here + duration = duration / 1000; + delay = delay / 1000; + UIViewAnimationOptions animationOptions; if ([direction isEqualToString:@"up"]) { animationOptions = UIViewAnimationOptionTransitionCurlUp; @@ -611,26 +637,24 @@ - (void) curl:(CDVInvokedUrlCommand*)command { animationOptions = UIViewAnimationOptionTransitionCurlDown; } else { CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"direction should be one of up|down"]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; return; } - if ([self loadHrefIfPassed:href]) { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - // remove the screenshot halfway during the transition - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ - [_screenShotImageView removeFromSuperview]; - }); - [UIView transitionWithView:self.viewController.view - duration:duration - options:animationOptions | UIViewAnimationOptionAllowAnimatedContent - animations:^{} - completion:^(BOOL finished) { - CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; - }]; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + // remove the screenshot halfway during the transition + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (duration/2) * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{ + [_screenShotImageView removeFromSuperview]; }); - } + [UIView transitionWithView:self.viewController.view + duration:duration + options:animationOptions | UIViewAnimationOptionAllowAnimatedContent + animations:^{} + completion:^(BOOL finished) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId]; + }]; + }); } - (BOOL) loadHrefIfPassed:(NSString*) href { From dc6d518f864aabb9136a11c6395fcf5bb3a47d32 Mon Sep 17 00:00:00 2001 From: kent Date: Tue, 13 Oct 2015 15:41:32 -0400 Subject: [PATCH 10/13] Refactoring NativePageTransitions.java class to use TransitionType enum. This will make it easier to handle different kinds of pending transitions --- src/android/NativePageTransitions.java | 129 +++++++++++++++---------- 1 file changed, 80 insertions(+), 49 deletions(-) diff --git a/src/android/NativePageTransitions.java b/src/android/NativePageTransitions.java index 83ed8b5..08e9e8b 100644 --- a/src/android/NativePageTransitions.java +++ b/src/android/NativePageTransitions.java @@ -33,6 +33,7 @@ public class NativePageTransitions extends CordovaPlugin { private float retinaFactor; private long duration; private long delay; + private TransitionType pendingTransitionType; private String drawerAction; private String drawerOrigin; private String direction; @@ -61,14 +62,9 @@ public class NativePageTransitions extends CordovaPlugin { public Object onMessage(String id, Object data) { if ("onPageFinished".equalsIgnoreCase(id)) { - if ("slide".equalsIgnoreCase(_action)) { - doSlideTransition(); - } else if ("fade".equalsIgnoreCase(_action)) { - doFadeTransition(); - } else if ("flip".equalsIgnoreCase(_action)) { - doFlipTransition(); - } else if ("drawer".equalsIgnoreCase(_action)) { - doDrawerTransition(); + TransitionType type = TransitionType.fromString(_action); + if (type != null) { + type.doTransition(this); } } return super.onMessage(id, data); @@ -121,16 +117,13 @@ public boolean execute(String action, JSONArray args, final CallbackContext call if ("executePendingTransition".equalsIgnoreCase(action)) { delay = 0; - if ("slide".equalsIgnoreCase(_action)) { - doSlideTransition(); - } else if ("fade".equalsIgnoreCase(_action)) { - doFadeTransition(); - } else if ("flip".equalsIgnoreCase(_action)) { - doFlipTransition(); - } else if ("drawer".equalsIgnoreCase(_action)) { - doDrawerTransition(); + if (pendingTransitionType != null) { + pendingTransitionType.doTransition(this); + pendingTransitionType = null; + return true; + } else { + return false; } - return true; } _action = action; @@ -193,15 +186,12 @@ public void run() { } } - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else if (delay > -1) { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doSlideTransition(); - } - } else if (delay > -1) { - doSlideTransition(); + } else { + pendingTransitionType = TransitionType.SLIDE; } } }); @@ -248,15 +238,12 @@ public void run() { bringToFront(imageView2); } - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else if (delay > -1) { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doDrawerTransition(); - } - } else if (delay > -1) { - doDrawerTransition(); + } else { + pendingTransitionType = TransitionType.DRAWER; } } }); @@ -272,15 +259,12 @@ public void run() { imageView.setImageBitmap(getBitmap()); bringToFront(imageView); - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else if (delay > -1) { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doFadeTransition(); - } - } else if (delay > -1) { - doFadeTransition(); + } else { + pendingTransitionType = TransitionType.FADE; } } }); @@ -295,15 +279,12 @@ public void run() { @Override public void run() { imageView.setImageBitmap(getBitmap()); - if (href != null && !"null".equals(href)) { - if (!href.startsWith("#") && href.contains(".html")) { + if (href != null && !"null".equals(href) && !href.startsWith("#") && href.contains(".html")) { webView.loadUrlIntoView(HREF_PREFIX + href, false); - } else if (delay > -1) { - // it's a #hash, which is handled in JS + } else if (shouldFireNow()) { doFlipTransition(); - } - } else if (delay > -1) { - doFlipTransition(); + } else { + pendingTransitionType = TransitionType.FLIP; } } }); @@ -311,7 +292,7 @@ public void run() { return true; } - private void doFadeTransition() { + public void doFadeTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -374,7 +355,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doFlipTransition() { + public void doFlipTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -444,7 +425,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doSlideTransition() { + public void doSlideTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -579,7 +560,7 @@ public void onAnimationRepeat(Animation animation) { }, delay); } - private void doDrawerTransition() { + public void doDrawerTransition() { if (!calledFromJS || this._callbackContext.getCallbackId().equals(lastCallbackID)) { return; } @@ -721,4 +702,54 @@ private void enableHardwareAcceleration() { } } } + + private boolean shouldFireNow() { + return delay >= 0; + } + + private enum TransitionType { + FLIP("flip") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doFlipTransition(); + } + }, + SLIDE("slide") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doSlideTransition(); + } + }, + FADE("fade") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doFadeTransition(); + } + }, + DRAWER("drawer") { + @Override + public void doTransition(NativePageTransitions plugin) { + plugin.doDrawerTransition(); + } + }; + + private String code; + + private TransitionType(String code) { + this.code = code; + } + + public abstract void doTransition(NativePageTransitions plugin); + + public static TransitionType fromString(String text) { + if (text != null) { + for (TransitionType type : TransitionType.values()) { + if (text.equalsIgnoreCase(type.code)) { + return type; + } + } + } + return null; + } + } } \ No newline at end of file From 8d9b871915dcd174afdb89c6bddfd4cc2b1899af Mon Sep 17 00:00:00 2001 From: kent Date: Tue, 13 Oct 2015 17:10:56 -0400 Subject: [PATCH 11/13] #62 - added function option for delay instead of integer to control when animating pending transition happens --- www/NativePageTransitions.js | 97 +++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index 33ac670..f56cd05 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -4,16 +4,14 @@ function NativePageTransitions() { NativePageTransitions.prototype.globalOptions = { duration: 400, iosdelay: 60, // a number of milliseconds, or -1 (call executePendingTransition() when ready) - androiddelay: 70, // a number of milliseconds, or -1 (call executePendingTransition() when ready) + androiddelay: 70, // a number of milliseconds, a function that takes an animate callback that will execute pending transition when invoked, or -1 to manually call executePendingTransition() when ready winphonedelay: 200, slowdownfactor: 4, fixedPixelsTop: 0, // currently for slide left/right only fixedPixelsBottom: 0 // currently for slide left/right only }; -NativePageTransitions.prototype.executePendingTransition = function (onSuccess, onError) { - cordova.exec(onSuccess, onError, "NativePageTransitions", "executePendingTransition", []); -}; +NativePageTransitions.prototype.executePendingTransition = executePendingTransition; NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { var opts = options || {}; @@ -24,15 +22,7 @@ NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); if (opts.fixedPixelsTop == undefined || opts.fixedPixelsTop == "null") { opts.fixedPixelsTop = this.globalOptions.fixedPixelsTop; } @@ -42,6 +32,7 @@ NativePageTransitions.prototype.slide = function (options, onSuccess, onError) { // setting slowdownfactor > 1 makes the next page slide less pixels. Use 1 for side-by-side. opts.slowdownfactor = opts.slowdownfactor || this.globalOptions.slowdownfactor; cordova.exec(onSuccess, onError, "NativePageTransitions", "slide", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.drawer = function (options, onSuccess, onError) { @@ -54,16 +45,9 @@ NativePageTransitions.prototype.drawer = function (options, onSuccess, onError) if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); cordova.exec(onSuccess, onError, "NativePageTransitions", "drawer", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.flip = function (options, onSuccess, onError) { @@ -75,16 +59,9 @@ NativePageTransitions.prototype.flip = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } - if (opts.winphonedelay == undefined || opts.winphonedelay == "null") { - opts.winphonedelay = this.globalOptions.winphonedelay; - } + var delayCallbacks = setupDelayOptions(opts); cordova.exec(onSuccess, onError, "NativePageTransitions", "flip", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.curl = function (options, onSuccess, onError) { @@ -96,10 +73,9 @@ NativePageTransitions.prototype.curl = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } + var delayCallbacks = setupDelayOptions(opts, {android: false, winphone: false}); cordova.exec(onSuccess, onError, "NativePageTransitions", "curl", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype.fade = function (options, onSuccess, onError) { @@ -110,13 +86,9 @@ NativePageTransitions.prototype.fade = function (options, onSuccess, onError) { if (opts.duration == undefined || opts.duration == "null") { opts.duration = this.globalOptions.duration; } - if (opts.androiddelay == undefined || opts.androiddelay == "null") { - opts.androiddelay = this.globalOptions.androiddelay; - } - if (opts.iosdelay == undefined || opts.iosdelay == "null") { - opts.iosdelay = this.globalOptions.iosdelay; - } + var delayCallbacks = setupDelayOptions(opts, {winphone: false}); cordova.exec(onSuccess, onError, "NativePageTransitions", "fade", [opts]); + fireDelayCallbacks(delayCallbacks); }; NativePageTransitions.prototype._validateHref = function (href, errCallback) { @@ -173,4 +145,49 @@ NativePageTransitions.install = function () { return window.plugins.nativepagetransitions; }; +function setupDelayOptions(opts, whichOS) { + whichOS = whichOS || {}; + whichOS = { + android: whichOS.android !== false, + ios: whichOS.ios !== false, + winphone: whichOS.winphone !== false + }; + var callbacks = []; + var globalOptions = NativePageTransitions.prototype.globalOptions; + if (whichOS.android) { + if (opts.androiddelay == undefined || opts.androiddelay == "null") { + opts.androiddelay = globalOptions.androiddelay; + } else if (typeof opts.androiddelay == 'function') { + callbacks.push(opts.androiddelay); + opts.androiddelay = -1; + } + } + if (whichOS.ios && (opts.iosdelay == undefined || opts.iosdelay == "null")) { + opts.iosdelay = globalOptions.iosdelay; + } + if (whichOS.winphone && (opts.winphonedelay == undefined || opts.winphonedelay == "null")) { + opts.winphonedelay = globalOptions.winphonedelay; + } + return callbacks; +} + +function animate(onSuccess, onError) { + onSuccess = onSuccess || function() {}; + onError = onError || function() {}; + executePendingTransition(onSuccess, onError); +} + +function fireDelayCallbacks(callbacks) { + if (callbacks) { + for (var i = 0; i < callbacks.length; i++) { + var callback = callbacks[i]; + callback(animate); + } + } +} + +function executePendingTransition(onSuccess, onError) { + cordova.exec(onSuccess, onError, "NativePageTransitions", "executePendingTransition", []); +}; + cordova.addConstructor(NativePageTransitions.install); \ No newline at end of file From 760193cd5a4232025913314904a859b39f87fe78 Mon Sep 17 00:00:00 2001 From: kent Date: Fri, 16 Oct 2015 14:54:39 -0400 Subject: [PATCH 12/13] Added ios delay capability to be a function --- www/NativePageTransitions.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index f56cd05..aba54c6 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -162,8 +162,13 @@ function setupDelayOptions(opts, whichOS) { opts.androiddelay = -1; } } - if (whichOS.ios && (opts.iosdelay == undefined || opts.iosdelay == "null")) { - opts.iosdelay = globalOptions.iosdelay; + if (whichOS.ios) { + if (opts.iosdelay == undefined || opts.iosdelay == "null")) { + opts.iosdelay = globalOptions.iosdelay; + } else if (typeof opts.iosdelay == 'function') { + callbacks.push(opts.iosdelay); + opts.iosdelay = -1; + } } if (whichOS.winphone && (opts.winphonedelay == undefined || opts.winphonedelay == "null")) { opts.winphonedelay = globalOptions.winphonedelay; From 0d69249b42f5fbb05fef229d06feddf78fb2185d Mon Sep 17 00:00:00 2001 From: kent Date: Fri, 16 Oct 2015 15:04:20 -0400 Subject: [PATCH 13/13] Remove extra paren --- www/NativePageTransitions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/NativePageTransitions.js b/www/NativePageTransitions.js index aba54c6..fd24140 100755 --- a/www/NativePageTransitions.js +++ b/www/NativePageTransitions.js @@ -163,7 +163,7 @@ function setupDelayOptions(opts, whichOS) { } } if (whichOS.ios) { - if (opts.iosdelay == undefined || opts.iosdelay == "null")) { + if (opts.iosdelay == undefined || opts.iosdelay == "null") { opts.iosdelay = globalOptions.iosdelay; } else if (typeof opts.iosdelay == 'function') { callbacks.push(opts.iosdelay);