9

I have a problem using Phonegap on iOS 11 on an iPad. If a select is clicked, it displays the options in a pop-up. After selecting one, the pop-up briefly disappears, the option in the select changes, then the pop-up re-appears. The following message is in the Xcode console:

[Warning] Application tried to represent an active popover presentation: <UIPopoverPresentationController: 0x100c3e450>

Edit: after the pop-up re-appears, nothing happens when you click it.

How can I get the select to not re-display the pop-up after selecting an option?

This is using the latest Phonegap 7.0.1.

It's just a normal html select:

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1, user-scalable=no" />
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' 'unsafe-eval'">
<script type="text/javascript" src="cordova.js"></script>
</head>
<body>
<select>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
</body>
</html>

You can download a sample project here:

https://github.com/tomkincaid/selecttest

I am running this by directly opening platforms/ios/SelectTest.xcodeproj in Xcode.

Edit: with two selects, the behavior is even stranger.

<select id="select1">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select id="select2">
<option value="4">Four</option>
<option value="5">Five</option>
<option value="6">Six</option>
</select>

Click on select1, it brings up the popup with select1 option.

Select an option, popup briefly disappears then reappears.

Click body to make pop up disappear.

Click select2. The select1 popup appears.

Click body to make pop up disappear. Popup briefly disappears, then reappears empty.

Click body to make pop up disappear.

Click select2 again. Now it displays the correct popup.

Tom Kincaid
  • 4,887
  • 6
  • 47
  • 72
  • Any workaround? I'm also facing same issue throughout the app – Anjana Oct 06 '17 at 07:28
  • @Anjana-Systematix As mentioned in the other answers 1) compile with Xcode 8, but not sure if the App Store will accept this or 2) intercept the click on the select and present a custom picker. – Tom Kincaid Oct 17 '17 at 11:26

3 Answers3

1

It seems this is an issue with UIWebVIew on iOS 11 for all apps on iPad not just Phonegap/Cordova. Since UIWebVIew is depreciated for WKWebView, it's unlikely that Apple will fix it. Until Phonegap/Cordova uses WKWebView, I hacked together this fix. Basically, it puts a div over the select then opens a picker from a custom plugin.

index.html

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1 maximum-scale=1, user-scalable=no" />
<meta http-equiv="Content-Security-Policy" content="default-src * 'unsafe-inline' 'unsafe-eval'">
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="PhonegapUtility.js"></script>
<script type="text/javascript">

function onBodyLoad() {
    document.addEventListener("deviceready", onDeviceReady, false);
}

function onDeviceReady() {
    addSelectButton('#selecta');
    addSelectButton('#selectb');
}

function addSelectButton(selectID) {
    var u = new PhonegapUtility();
    u.isIpad(function(resp){
        if (resp == 1) {
            var buttonID = selectID+"Button";
            if($(buttonID).length == 0) {
                $("body").append("<div id='"+buttonID.replace("#","")+"' onclick='showPicker(\""+selectID+"\");'></div>");
            }
            $(buttonID).css("position","absolute");
            $(buttonID).css("left",$(selectID).offset().left+"px");
            $(buttonID).css("top",""+$(selectID).offset().top+"px");
            $(buttonID).css("width",$(selectID).width()+"px");
            $(buttonID).css("height",$(selectID).height()+"px");
            // need to adjust this for margin and padding
        }
    });
}

function showPicker(selectID) {
    var optionArray = [];
    $(selectID).find('option').each(function(index,element){
        optionArray.push(element.text);
    });
    var u = new PhonegapUtility();
    u.showPicker(optionArray.join("|||"),$(selectID).prop('selectedIndex'),function(resp){
        $(selectID+" option")[resp].selected = true;
    });
}

</script>
</head>
<body onload="onBodyLoad();">

<select id="selecta">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>

<select id="selectb">
<option value="4">Option 4</option>
<option value="5">Option 5</option>
<option value="6">Option 6</option>
</select>

</body>
</html>

PhonegapUtility.h

#import <Cordova/CDV.h>
@interface PhonegapUtility : CDVPlugin <UIPickerViewDelegate,UIPickerViewDataSource>
@property (strong, nonatomic) NSString *callbackId;
@property (strong, nonatomic) UIPickerView *pickerView;
@property (strong, nonatomic) UIView *pickerWrappertView;
@property (strong, nonatomic) NSArray *pickerData;
- (void) isIpad:(CDVInvokedUrlCommand*)command;
- (void) showPicker:(CDVInvokedUrlCommand*)command;
- (void) pickerDone;
@end

PhonegapUtility.m

#import "PhonegapUtility.h"
#import "AppDelegate.h"

@implementation TomPhonegapUtility

@synthesize callbackId,pickerData,pickerView,pickerWrappertView;

- (void) isIpad:(CDVInvokedUrlCommand*)command {
    int iPad = 0;
    if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) iPad = 1;
    CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:iPad];
    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

- (void) showPicker:(CDVInvokedUrlCommand*)command {

    callbackId = [[NSString alloc] initWithString: command.callbackId];

    AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
    UIViewController *rootViewController = appDelegate.window.rootViewController;

    pickerData = [[command.arguments objectAtIndex:0] componentsSeparatedByString:@"|||"];

    float viewWidth = rootViewController.view.bounds.size.width; //[UIScreen mainScreen].bounds.size.width;
    float viewHeight = rootViewController.view.bounds.size.height; //[UIScreen mainScreen].bounds.size.height;

    UIToolbar *toolBar= [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, viewWidth, 44)];
    [toolBar setBarStyle:UIBarStyleDefault];
    UIBarButtonItem *flex = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
    UIBarButtonItem *barButtonDone = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                                      style:UIBarButtonItemStylePlain
                                                                     target:self
                                                                     action:@selector(pickerDone)];
    toolBar.items = @[flex, barButtonDone];

    pickerView = [[UIPickerView alloc] init];
    [pickerView setDataSource: self];
    [pickerView setDelegate: self];
    [pickerView setFrame: CGRectMake(0, toolBar.frame.size.height, viewWidth, 180.0f)];
    pickerView.showsSelectionIndicator = YES;

    [pickerView selectRow:[[command.arguments objectAtIndex:1] intValue] inComponent:0 animated:NO];

    pickerWrappertView = [[UIView alloc] initWithFrame:CGRectMake(0, viewHeight-toolBar.frame.size.height-pickerView.frame.size.height, viewWidth, toolBar.frame.size.height + pickerView.frame.size.height)];
    pickerWrappertView.backgroundColor = [UIColor whiteColor];
    [pickerWrappertView addSubview:pickerView];
    [pickerWrappertView addSubview:toolBar];

    [rootViewController.view addSubview:pickerWrappertView];
}

- (void) pickerDone {
    int selectedIndex = (int) [pickerView selectedRowInComponent:0];
    CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:selectedIndex];
    [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
    [pickerWrappertView removeFromSuperview];
}

-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
    return 1;
}

-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
    return [pickerData count];
}

-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
    return [pickerData objectAtIndex:row];
}

-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
}
Tom Kincaid
  • 4,887
  • 6
  • 47
  • 72
  • In my actual production implementation, it's a bit more complicated. I'm using Jquery mobile, which wraps the selects in it's own divs, so I put the overlay in the wrapper so it scrolls with the select. I check if a picker is already presented and remove it before showing a new picker. So far it's working fairly well and I actually prefer the iPhone style picker over the iPad style alert. – Tom Kincaid Oct 17 '17 at 11:33
  • Do you have a link to add the plugin? How do we get the callback from the select option selected from the dropdown? – sSD Dec 12 '17 at 09:03
  • The code is above. You can customize it how you want. You get the callback by clicking the "done" button. – Tom Kincaid Dec 12 '17 at 15:36
1

Can you try to build your application with the release flag? Something like:

cordova build --release ios

I'm able to reproduce the issue without that release flag, but with it, the problem disappear. Therefore I'm wondering about the real source of that issue: is it really on Apple side? Seems to be on Cordova side... or maybe I'm missing something.

Some informations about my environement may be usefull:

  • iOS 11.0.1
  • Cordova CLI 6.5.0
  • Cordova-iOS 4.4.0

EDIT: my bad, it's not related to the release build at all, it's only because my release build is done on another Mac, running on XCode 8. Indeed, the issue only appears when you build the application with XCode 9. Building application for iOS11 from XCode 8 is working, so my suggestion is to use XCode 8 until we've got a solid workaround either on Apple or Cordova side.

Source: https://forums.developer.apple.com/thread/88169

Sébastien BATEZAT
  • 2,353
  • 26
  • 42
  • 1
    Thanks for the advice about Xcode 8. The issue i definitely with Apple. I made a test app that consisted of just a UIWebView and the same problem happened. – Tom Kincaid Oct 04 '17 at 10:24
  • Any alternate solution? please help... as throughout my app this issue is appearing... – Anjana Oct 06 '17 at 07:29
  • AFAIK, the only alternate solution is to rewrite the select component. Two choices: from native side (cordova plugin for the iOS platform - there is an example posted above by @TomKincaid) or from JS side (AngularJS directive seems to be a reasonnable way to go, just google it and you'll find many examples) – Sébastien BATEZAT Oct 06 '17 at 07:50
1

Just ran into this glitch. I found this plugin that may help everyone:

https://github.com/apache/cordova-plugin-wkwebview-engine

Basically forces Cordova to use WKWebView. Just a disclaimer... I haven't run our app through a proper soak test after using this so I don't know if this will cause other issues, but it does fix the select problem.

Currently on Cordova v.7.0.1 and Cordova iOS platform 4.4.0

Ting Sun
  • 306
  • 2
  • 10
  • Ok so after a ton of testing there are some SERIOUS limitations with WKWebview. Can't log JS errors anymore in console (just gives a general script error at line 0 no matter what happens). WKWebview also does not play well with dataStorage... I can't seem to retrieve any images I store locally, just gives a blank. Also see a few issues with localStorage. If you guys plan to use this plugin, soak test the crap out your application before releasing it. I had to roll back to not using it. – Ting Sun Nov 17 '17 at 00:54