Ionic Popover Works on XCode Simulator but not on iPhone - ios

I have an Ionic Popover on my app. The popover appears when I run ionic serve, ionic emulate ios, and the XCode simulator. However, as soon as I simulate the app to my iPhone 4S on XCode or use the Ionic View app to view my own application, the popover does not appear. I've debugged EVERYTHING and the code does not work. No errors appear on my console when running the app.
Once in a blue moon the popover will appear on my 4S, but there is no logic to how the popover appears. I would change a piece of code, the popover appears, then when I change it again, the popover disappears. I repeat this process and go back to my old code version that worked and it doesn't work. This is frustrating. What's worse I fear no one will respond to this message. Any help as to why there is a discrepancy between the iPhone simulator and my actual iPhone would be great. Thanks.
Button HTML
<div ng-controller="FilterPopoverController as filterPopover" class="text-right">
<div on-tap="filterPopover.open()" ng-class="{filterButtonOpened: filterPopover.opened}" id="filter-button">
<span class="assertive" >
<i class="icon ion-arrow-down-b"></i>
<span class="bold">FILTER</span>
</span>
</div>
</div>
Popover HTML
<ion-popover-view id="filterPopover">
<ion-header-bar class="bar-dark">
<h1 id="popoverTitle" class="bold">FILTER BY</h1>
</ion-header-bar>
<ion-content>
<p>Content here</p>
</ion-content>
</ion-popover-view>
The Popover Controller
.controller('FilterPopoverController', filterPopoverController)
filterPopoverController.$inject = ['$ionicPopover', '$filter', '$scope', '$timeout'];
function filterPopoverController($ionicPopover, $filter, $scope, $timeout) {
var vm = this;
vm.open = open;
vm.popover = null;
vm.opened = false;
activate();
//Cleanup the popover when we're done with it!
$scope.$on('$destroy', function() {
vm.popover.remove();
vm.opened = false;
});
$scope.$on('popover.hidden', function() {
vm.opened = false;
});
function activate( ) {
$ionicPopover.fromTemplateUrl('/templates/search/filter-popover.html', {
scope: $scope
}).then(function(popover) {
vm.popover = popover;
});
}
function open( ) {
vm.opened = true;
vm.popover.show();
}
}
I've had to remove sensitive info from some of this code but this is the gist of it.

I have made two modifications to your posted code:
first one is to change the path of popover template to be:
'templates/search/filter-popover.html'
instead of
'/templates/search/filter-popover.html'
You need to reference this file starting from current directory instead of root directory
Second changed is to pass $event input while opening the popover, this is from official documentation of ionic Popover
After applying both of these changes to the posted code, I manage to see popover on desktop browser, ios simulator, real iPhone 4 consistenly
Here is the final code:
angular.module('starter', ['ionic'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.controller('FilterPopoverController', filterPopoverController)
filterPopoverController.$inject = ['$ionicPopover', '$filter', '$scope', '$timeout'];
function filterPopoverController($ionicPopover, $filter, $scope, $timeout) {
var vm = this;
vm.open = open;
vm.popover = null;
vm.opened = false;
activate();
//Cleanup the popover when we're done with it!
$scope.$on('$destroy', function() {
vm.popover.remove();
vm.opened = false;
});
$scope.$on('popover.hidden', function() {
vm.opened = false;
});
function activate( ) {
$ionicPopover.fromTemplateUrl('templates/search/filter-popover.html', {
scope: $scope
}).then(function(popover) {
vm.popover = popover;
});
}
function open( $event ) {
vm.opened = true;
vm.popover.show($event);
}
}
<div ng-controller="FilterPopoverController as filterPopover" class="text-right">
<div on-tap="filterPopover.open($event)" ng-class="{filterButtonOpened: filterPopover.opened}" id="filter-button">
<span class="assertive" >
<i class="icon ion-arrow-down-b"></i>
<span class="bold">FILTER</span>
</span>
</div>
</div>
I hope that this solves your problem.

Related

How to show hidden button by *ngIf in iPhone X

I am creating a simple screen with ion-slides. On the last slide, i want to show a button to pop the screen.
I already accomplished this using the code below:
In my HTML
<section>
<ion-slides (ionSlideDidChange)="change()" (ionSlideReachEnd)="end()" class="slider" pager="true" paginationType="bullet" parallax="true"
centeredSlides="true" effect="slide" [options]="slideOpts">
<ng-container *ngFor="let image of imgs;">
<ion-slide>
<section>
<h1><img src={{image}}></h1>
</section>
</ion-slide>
</ng-container>
</ion-slides>
<div class="skip" *ngIf="!isSlideLast()" (click)="skip()">Skip</div>
<div class="next" *ngIf="!isSlideLast()" (click)="next()">Next</div>
<button class="start" *ngIf="isSlideLast()" (click)="skip()">Start</button>
</section>
In my .ts, I have these:
max_slide:any =3;
last: boolean = false;
callback: any;
imgs:any = [];
slideOpts = {
initialSlide: 0,
speed: 400
};
isSlideLast() {
return this.last
}
async skip() {
console.log("skip is pressed")
this.callback().then(() => {
this.navCtrl.pop();
});
}
change() {
this.slides.getActiveIndex().then(val => {
console.log(val)
if (val < this.max_slide-1) {
this.last = false;
}
})
}
end() {
console.log("end is pressed")
this.last = true;
}
next() {
console.log("next is pressed")
this.slides.slideNext();
}
There is no problem with other iPhones and android. The button shows on the last slide. But when testing with iPhone X, the button is not showing at all. The skip and next link also doesn't hide.

React Bootstrap OverlayTrigger with trigger="focus" bug work around

In iOS safari, OverlayTrigger with trigger="focus" isn't able to dismiss when tapping outside. Here is my code:
<OverlayTrigger
trigger="focus"
placement="right"
overlay={ <Popover id="popoverID" title="Popover Title">
What a popover...
</Popover> } >
<a bsStyle="default" className="btn btn-default btn-circle" role="Button" tabIndex={18}>
<div className="btn-circle-text">?</div>
</a>
</OverlayTrigger>
I know that this is a known bug for Bootstrap cuz this doesn't even work on their own website in iOS, but does anyone know any method to go around it? It would be the best if it is something that doesn't require jQuery, but jQuery solution is welcome. Thanks.
OK, since no one else gives me a work around, I worked on this problem with my co-worker together for 3 days, and we came up with this heavy solution:
THE PROBLEM:
With trigger="focus", Bootstrap Popover/Tooltip can be dismissed when CLICKING outside the Popover/Tooltip, but not TOUCHING. Android browsers apparently changes touches to clicks automatically, so things are fine on Android. But iOS safari and browsers that is based on iOS safari (iOS chrome, iOS firefox, etc...) don't do that.
THE FIX:
We found out that in React Bootstrap, the Overlay component actually lets you customize when to show the Popover/Tooltip, so we built this component InfoOverlay based on Overlay. And to handle clicking outside the component, we need to add event listeners for both the Popover/Tooltip and window to handle both 'mousedown' and 'touchstart'. Also, this method would make the Popover have its smallest width all the time because of the padding-right of the component is initially 0px, and we make based on the width of some parent component so that it is responsive based on the parent component. And the code looks like this:
import React, { Component, PropTypes as PT } from 'react';
import {Popover, Overlay} from 'react-bootstrap';
export default class InfoOverlay extends Component {
static propTypes = {
PopoverId: PT.string,
PopoverTitle: PT.string,
PopoverContent: PT.node,
// You need to add this prop and pass it some numbers
// if you need to customize the arrowOffsetTop, it's sketchy...
arrowOffsetTop: PT.number,
// This is to be able to select the parent component
componentId: PT.string
}
constructor(props) {
super(props);
this.state = {
showPopover: false,
popoverClicked: false
};
}
componentDidMount() {
// Here are the event listeners and an algorithm
// so that clicking popover would not dismiss itself
const popover = document.getElementById('popoverTrigger');
if (popover) {
popover.addEventListener('mousedown', () => {
this.setState({
popoverClicked: true
});
});
popover.addEventListener('touchstart', () => {
this.setState({
popoverClicked: true
});
});
}
window.addEventListener('mousedown', () => {
if (!this.state.popoverClicked) {
this.setState({
showPopover: false
});
} else {
this.setState({
popoverClicked: false
});
}
});
window.addEventListener('touchstart', () => {
if (!this.state.popoverClicked) {
this.setState({
showPopover: false
});
} else {
this.setState({
popoverClicked: false
});
}
});
// this is to resize padding-right when window resizes
window.onresize = ()=>{
this.setState({});
};
}
// This function sets the style and more importantly, padding-right
getStyle() {
if (document.getElementById(this.props.componentId) && document.getElementById('popoverTrigger')) {
const offsetRight = document.getElementById(this.props.componentId).offsetWidth - document.getElementById('popoverTrigger').offsetLeft - 15;
return (
{display: 'inline-block', position: 'absolute', 'paddingRight': offsetRight + 'px'}
);
}
return (
{display: 'inline-block', position: 'absolute'}
);
}
overlayOnClick() {
this.setState({
showPopover: !(this.state.showPopover)
});
}
render() {
const customPopover = (props) => {
return (
{/* The reason why Popover is wrapped by another
invisible Popover is so that we can customize
the arrowOffsetTop, it's sketchy... */}
<div id="customPopover">
<Popover style={{'visibility': 'hidden', 'width': '100%'}}>
<Popover {...props} arrowOffsetTop={props.arrowOffsetTop + 30} id={this.props.PopoverId} title={this.props.PopoverTitle} style={{'marginLeft': '25px', 'marginTop': '-25px', 'visibility': 'visible'}}>
{this.props.PopoverContent}
</Popover>
</Popover>
</div>
);
};
return (
<div id="popoverTrigger" style={this.getStyle()}>
<a bsStyle="default" className="btn btn-default btn-circle" onClick={this.overlayOnClick.bind(this)} role="Button" tabIndex={13}>
<div id="info-button" className="btn-circle-text">?</div>
</a>
<Overlay
show={this.state.showPopover}
placement="right"
onHide={()=>{this.setState({showPopover: false});}}
container={this}>
{customPopover(this.props)}
</Overlay>
</div>
);
}
}
In the end, this is a heavy work around because it is a big amount of code for a fix, and you can probably feel your site is slowed down by a tiny bit because of the 4 event listeners. And the best solution is just tell Bootstrap to fix this problem...

Ionic Framework textarea keyboard scroll issue after Apple Testflight processing

We are using the ionic framework with iOS.
In the iOS emulator and in Safari browser, for one of our pages, clicking in a textarea shows the keyboard, and scrolls the textarea upwards so it is still viewable.
When the app is archived and processed through Apple iOS TestFlight, the behaviour is changed. Now, clicking in a textarea shows the keyboard, but the textarea no longer scrolls upwards so it is hidden.
Looks like something in the archival process is causing an issue.
Here's the code (there's another div above it). There's only the one textarea.
<div ng-if="!dy_compl">
<div class="wi-bottom item ">
<div ng-repeat="(key, dy) in element.dys">
<div id="wi-scroll-div" ng-if="key == dySel" style="height: {{scroller_height}}; overflow: scroll;">
<div>
<style>
p.wi-icon:before {
background: url("img/old_building.png") no-repeat !important;
}
</style>
<p class="wi-icon" ng-bind-html="dy.intro | to_trusted"></p>
</div>
<div ng-if="dy.ref">
<p class="wi-intro-my3" ng-bind-html="dy.ref.intro | to_trusted"></p>
<div ng-repeat="data in dy.ref.data track by $index">
<p class="wi-intro-my3-table" style="margin-left: 5%;" ng-bind-html="data | to_trusted"></p>
</div>
</div>
<label id="wi-input" class="item item-input item-stacked-label">
<span class="input-label" style="width:100%; max-width: 100%;">
<div class="wi-bottom-input-label" ng-bind-html="dy.notelabel | to_trusted"></div>
</span>
<textarea class="wi-bottom-input" ng-model="dy.note" type="text" placeholder="{{dy.note}}" ng-style="{'background-color': textAreaBackgroundColor}"></textarea>
</label>
<button class="wi-bottom-button button button-assertive col text-center" ng-click="dy.saved=true;saveNow()">
Save Notes
</button>
</br>
</div>
</div>
</div>
If you can't make it work with the plugin, check out this code I used on one project.. Looks a bit overhead, but it works:
Template:
<ion-content scroll-handle="user-profile-scroll">
<textarea maxlength="160" ng-model="currentUser.bio" ng-readonly="!editMode || focusAddInterestInput" placeholder="Write your bio..." class="user-bio">< </textarea>
</ion-content>
Controller:
$scope.windowHeight = window.innerHeight;
$scope.keyboardHeight = 0;
$scope.$on('$ionicView.loaded', function() {
var scrollView = {scrollTo: function() { console.log('Could not resolve scroll delegate handle'); }};
$timeout(function() {
var instances = $ionicScrollDelegate.$getByHandle('user-profile-scroll')._instances;
instances.length && (scrollView = instances[instances.length - 1]);
}).then(function() {
$scope.unbindShowKeyboardHandler = $scope.$on('KeyboardWillShowNotification', function(evt, info) {
$scope.keyboardHeight = info.keyboardHeight;
var input = angular.element(document.activeElement);
var body = angular.element(document.body);
var top = input.prop('offsetTop');
var temp = angular.element(input.prop('offsetParent'));
var tempY = 0;
while (temp && typeof(temp.prop('offsetTop')) !== 'undefined') {
tempY = temp.prop('offsetTop');
top += tempY;
temp = angular.element(temp.prop('offsetParent'));
}
top = top - (scrollView.getScrollPosition().top || 0);
var inputHeight = input.prop('offsetHeight');
var requiredSroll = $scope.windowHeight - $scope.keyboardHeight > top + inputHeight + 11 ? 0 : $scope.windowHeight - $scope.keyboardHeight - top - inputHeight - 12;
$timeout(function(){ scrollView.scrollTo(0, - requiredSroll || 0, true); });
});
$scope.unbindHideKeyboardHandler = $scope.$on('KeyboardWillHideNotification', function(evt, info) {
console.log(evt, info);
$scope.keyboardHeight = 0;
$timeout(function() { scrollView.scrollTo(0, 0, true); });
});
$scope.$on('$destroy', function() {
$scope.unbindShowKeyboardHandler();
$scope.unbindHideKeyboardHandler();
});
});
});
andm finally in app.js:
window.addEventListener('native.keyboardshow', keyboardShowHandler);
window.addEventListener('native.keyboardhide', keyboardHideHandler);
function keyboardShowHandler(info){
$rootScope.$broadcast('KeyboardWillShowNotification', info);
}
function keyboardHideHandler(info){
$rootScope.$broadcast('KeyboardWillHideNotification', info);
}
Turns out that we had one view that was manually disabling the keyboard scroll using:
cordova.plugins.Keyboard.disableScroll(true)
We weren't re-enabling this on a switch to another view.
The result is that in the emulator, the scroll disabling didn't traverse the scope to the new page, whereas after archival and TestFlight, it did.
Thanks for the other answers, comments.

anchor tag not working in safari (ios) for iPhone/iPod Touch/iPad

This is what I have on my HTML5
<div class="google-play">
<a href="http://example.com" role="button">
<img src="img/google-play-btn.png"/>
</a>
</div>
and works fine on chrome, FF, android but doesn't seem to work on iPad.
Use touchend event via jQuery on all anchor tags. For example:
$(function () {
$('a').on('click touchend', function() {
var link = $(this).attr('href');
window.open(link,'_blank'); // opens in new window as requested
return false; // prevent anchor click
});
});
If you want to make the above iPhone and iPad specific function only, check to see if the "device" is an iPad, iPhone, etc. Like so:
$(function () {
IS_IPAD = navigator.userAgent.match(/iPad/i) != null;
IS_IPHONE = (navigator.userAgent.match(/iPhone/i) != null) || (navigator.userAgent.match(/iPod/i) != null);
if (IS_IPAD || IS_IPHONE) {
$('a').on('click touchend', function() {
var link = $(this).attr('href');
window.open(link,'_blank'); // opens in new window as requested
return false; // prevent anchor click
});
}
});

Jquery mobile show/hide div on tap

I have a jquery mobile page that contains a button that when tapped should show/hide a div. I seem to be missing something. I've worked through similar questions on SO with no success, can anyone suggest where I'm going wrong?
HTML:
View more filters
<div id="filters"> Blah </div>
CSS:
div#filters {
display: none;
}
JQ:
$('#myPage').live('pageinit', function(event) {
$("#moreFilters").bind('tap',function(event, ui){
$('#filters').toggle('fast', function() {});
})
});
I've also tried:
$('#moreFilters').live('tap',function(event) {
$("#filters").toggle(); // toggles the visibility/display of the element.
});
Could anyone point me in the right direction?
Many thanks in advance
can you do something like this,
<script>
var isMenuVisible = true
function showHideMenu() {
isMenuVisible = !isMenuVisible;
var menuPrin= document.getElementById("divMenu");
if(isMenuVisible) {
menuPrin.style.visibility = "visible";
menuPrin.style.width = "30%";
} else {
menuPrin.style.visibility = "hidden";
menuPrin.style.width = "0%";
}
}
</script>
This script could you put it at end of your page.
Now, point you id="divMenu" to your div that want hide, ok, and onclick="showHideMenu();" function on element that fire event.
All this simulate it tap and toggle events.
I hope this helps. :)

Resources