Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phonegap / Cordova on iOS 11 select displays pop-up again after selecting option

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.

like image 898
Tom Kincaid Avatar asked Sep 22 '17 18:09

Tom Kincaid


3 Answers

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{
}
like image 78
Tom Kincaid Avatar answered Oct 11 '22 20:10

Tom Kincaid


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

like image 20
Sébastien BATEZAT Avatar answered Oct 11 '22 18:10

Sébastien BATEZAT


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

like image 42
Ting Sun Avatar answered Oct 11 '22 19:10

Ting Sun