I'm working on a React Native app built with Expo and am trying to get the location function to work. While testing it out using Expo Go, I am asked for permissions to use the location feature, but when I build it out an IOS build and test on testflight, I'm never asked for permission. I've tried looking in settings to add the permission, but location isn't even an option.
Where I ask for permission in the app
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
}, []);
}, []);
IOS section of app.json:
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.nawdevelopment.discgolfgames",
"buildNumber": "1.0.4",
"infoPlist":{
"NSLocationUsageDescription":"Disc Golf Games uses location to determine distances, which is used for several games",
"NSLocationWhenInUseUsageDescription":"Disc Golf Games uses location to determine distances, which is used for several games",
"NSLocationAlwaysUsageDescription":"Disc Golf Games uses location to determine distances, which is used for several games",
"NSLocationUsageDescription":"Disc Golf Games uses location to determine distances, which is used for several games",
"UIBackgroundModes": [
"location",
]
}
},
package.json:
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"#react-native-community/masked-view": "^0.1.11",
"#unimodules/core": "~7.2.0",
"#unimodules/react-native-adapter": "~6.5.0",
"ansi-regex": "^4.1.1",
"expo": "^44.0.0",
"expo-linear-gradient": "~11.0.3",
"expo-location": "~14.0.1",
"expo-permissions": "^13.2.0",
"expo-status-bar": "~1.2.0",
"expo-task-manager": "~10.1.0",
"expo-updates": "~0.11.7",
"minimist": "^1.2.6",
"node-fetch": "^2.6.1",
"plist": "^3.0.5",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-elements": "^3.4.1",
"react-native-gesture-handler": "~2.1.0",
"react-native-reanimated": "~2.3.1",
"react-native-safe-area-context": "3.3.2",
"react-native-screens": "~3.10.1",
"react-native-web": "0.17.1",
"react-navigation": "^4.4.4",
"react-navigation-stack": "^2.10.4",
"react-navigation-tabs": "^2.11.1",
"unimodules-permissions-interface": "^6.1.0",
"url-parse": "1.5.10"
},
"devDependencies": {
"#babel/core": "^7.12.9"
},
"private": true
}
Looking at the anonymous function in your useEffect, it isn't called correctly.
Anonymous functions need () after them to be called. Example:
(function() {
console.log('Hi!');
})();
Source: https://www.javascripttutorial.net/javascript-anonymous-functions/
I took a look at Expo's docs as well and I believe what you are trying to accomplish in your useEffect is the following:
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
})();
}, []); //You have this line twice instead and are missing the line above this one
Source: https://docs.expo.dev/versions/v45.0.0/sdk/location/#usage
Without the () your anonymous function in your useEffect isn't being called, which is probably why you're never asked for permission.
As for your app.json and package.json, it looks like you have everything properly installed. What you should make sure is that in the "plugins" section of app.json you have expo-location showing up.
Related
I've already seen every stack question about this issue but nothing works. Already changed versions, fonts, type of fonts and the "old" Font.loadAsync. My code is basically equal of the offcial documentation: Font Expo Documentation
The error:
My device is IOS. Here's my code:
import { useCallback} from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import Menu from './screens/menu';
import FEMIntro from './screens/femIntro';
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
// Keep the splash screen visible while we fetch resources
SplashScreen.preventAutoHideAsync();
export default function App() {
let [fontsLoaded] = useFonts({
'Inter-Black': require('./assets/fonts/Inter-Black.otf'),
//'WorkSansRegular': require('./assets/fonts/WorkSans-Regular.ttf'),
});
const onLayoutRootView = useCallback(async () => {
if (fontsLoaded) {
await SplashScreen.hideAsync();
}
}, [fontsLoaded]);
if (!fontsLoaded) {
return <Text style={{ fontFamily: 'Inter-Black', fontSize: 30, color: '#000' }}>A
carregar...</Text>;
}
const Stack = createNativeStackNavigator();
return (
<View onLayout={onLayoutRootView}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Menu" component={Menu}/>
<Stack.Screen
name="FEM Intro"
component={FEMIntro}
/>
</Stack.Navigator>
</NavigationContainer>
</View>
);}
My package.json:
{
"name": "millervillagetycoon",
"version": "1.0.0",
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start --tunnel",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},
"dependencies": {
"#react-navigation/native": "^6.1.4",
"#react-navigation/native-stack": "^6.9.10",
"expo": "~47.0.12",
"expo-app-loading": "^2.1.1",
"expo-font": "~11.0.1",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2",
"react": "18.1.0",
"react-native": "0.70.5",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-native-svg-transformer": "^1.0.0",
"react-native-web": "~0.18.9",
"react-dom": "18.1.0",
"#expo/webpack-config": "^0.17.2"
},
"devDependencies": {
"#babel/core": "^7.12.9"
},
"private": true
}
Thank you.
I updated from Vue2 to Vue3 and now kind of stuck. I use it inside Rails.
The problem that everything is loading and compiling, not errors at all. I load my web page and see everything instead of Vue files. Don't understand how to fix it at all :(
Any ideas? Maybe I don't see smth or don't know where to looking for it.
If I change smth on the application file it shows some changes or errors. I even deleted everything and add Vue files like in tutorials shows and still nothing to work.
application.js
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
import Routes from '../routes/index.js.erb';
import '../css/index.css'
window.Routes = Routes;
import {createApp} from 'vue';
import Customer from '../customer.vue'
import CustomerSearch from '../customer_search.vue'
import CustomerPackages from '../customer_packages.vue'
import BusinessCases from '../business_cases.vue'
import SearchPanel from '../components/business_case/SearchPanel.vue'
import {turbolinksAdapterMixin} from "vue-turbolinks";
import Clipboard from 'v-clipboard'
document.addEventListener('turbolinks:load', () => {
const app = createApp({
el: "[data-behavior='vue']",
mixins: [turbolinksAdapterMixin],
})
})
app.component('customer', Customer);
app.component('customer-search', CustomerSearch);
app.component('packages', CustomerPackages);
app.component('business-cases', BusinessCases);
app.component('search-panel', SearchPanel);
envirement.js
const { environment } = require('#rails/webpacker')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
const erb = require('./loaders/erb')
const pug = require('./loaders/pug')
environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.prepend('vue', vue)
environment.loaders.prepend('erb', erb)
environment.loaders.prepend('pug', pug)
module.exports = environment
package.json
{
"name": "backyard",
"private": true,
"dependencies": {
"#rails/actioncable": "^6.0.0",
"#rails/activestorage": "^6.0.0",
"#rails/ujs": "^6.0.0",
"#rails/webpacker": "5.2.1",
"#tailwindcss/aspect-ratio": "^0.2.0",
"#tailwindcss/forms": "^0.2.1",
"#tailwindcss/typography": "^0.4.0",
"#vue/cli": "^5.0.0-alpha.8",
"axios": "^0.21.0",
"css-loader": "^5.0.2",
"dayjs": "^1.10.4",
"litepie-datepicker": "^1.0.13",
"node-sass": "4.14",
"pug": "^3.0.0",
"pug-plain-loader": "^1.1.0",
"rails-erb-loader": "^5.5.2",
"sass": "^1.32.7",
"sass-loader": "^11.0.1",
"turbolinks": "^5.2.0",
"v-clipboard": "^2.2.3",
"vue": "^3.0.2",
"vue-clickaway": "^2.2.2",
"vue-clipboards": "^1.3.0",
"vue-fuse": "^2.2.1",
"vue-loader": "^16.2.0",
"vue-nav-tabs": "^0.5.7",
"vue-pdf": "^4.2.0",
"vue-pug": "^4.0.0",
"vue-turbolinks": "^2.1.0",
"webpack": "4"
},
"version": "0.1.0",
"devDependencies": {
"#tailwindcss/postcss7-compat": "^2.0.3",
"#vue/compiler-sfc": "^3.0.11",
"#webpack-cli/serve": "^1.3.0",
"autoprefixer": "9",
"postcss": "7",
"tailwindcss": "npm:#tailwindcss/postcss7-compat",
"vue-cli-plugin-pug": "~2.0.0",
"webpack-cli": "3.3.12",
"webpack-dev-server": "^3.11.2"
},
"browserslist": [
"defaults"
]
}
I've had a similar problem and my best guess is the turbolinksAdapterMixin. Vue 3 changed and renamed destroy hooks to unmounted hooks (see here) besides other changes to their Component APIs. I believe, the package vue-turbolinks still uses Vue 2 functionalities, such as $on, $off, and $once for events.
The way Turbolinks works is that the "next HTML body" is fetched via AJAX which in turn replaces the document.body.
So, what you need to do – and already did correctly – is to create the Vue 3 App with createApp in the event listener for turbolinks:load (when the body was successfully replaced).
And then you have to tear down, i.e., unmount, your Vue app as soon as you leave the page (i.e., click a turbolinks link) with unmount().
Hence, you have to replace your code:
document.addEventListener('turbolinks:load', () => {
const app = createApp({
el: "[data-behavior='vue']",
mixins: [turbolinksAdapterMixin],
})
})
with this:
let app;
document.addEventListener('turbolinks:load', () => {
app = createApp({});
app.mount("[data-behavior='vue']");
});
// this event listener is executed as soon as
// the new body was fetched successfully but
// before replacing the `document.body`
document.addEventListener('turbolinks:before-render', () => {
if (app) app.unmount();
});
My project is working and build correctly in local, in development or production, but when I try to push to my DigitalOcean Ubuntu server through Dokku it crashes
# server.js at the root
const express = require('express');
const path = require('path');
const serveStatic = require('serve-static');
let app = express();
app.use(serveStatic(__dirname + "/dist"));
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log('Listening on port ' + port)
});
# current vue.config.js
# note that it had no option originally but changing it didn't solve the problem
const path = require('path')
module.exports = {
publicPath: '/',
configureWebpack: {
resolve: {
alias: {
'#': path.resolve(__dirname, 'src/')
}
}
},
css: {
loaderOptions: {
sass: {
data: `#import "src/assets/styles/helpers.scss";`
}
}
},
pluginOptions: {}
}
And here's the crash
- Building for production...
ERROR Failed to compile with 24 errors2:31:56 PM
These dependencies were not found:
* #/components/Errors/DefaultError in ./src/main.js
* #/components/Layouts/ChatLayout in ./src/main.js
* #/components/Layouts/DashboardLayout in ./src/main.js
* #/components/Layouts/DefaultLayout in ./src/main.js
* #/mixins/CurrentIdentityMixin in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/ConnectRouting.vue?vue&type=script&lang=js&, ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Chat/Events/File.vue?vue&type=script&lang=js& and 16 others
* #/mixins/LayoutMixin in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=js&, ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/ConnectRouting.vue?vue&type=script&lang=js&
This is very strange, it's as # wasn't recognised as shortcut in Vue, but I don't really know how to fix it. I think setting everything with relative path manually would solve the issue (it worked at least for the layouts when I tried); it's not really a fix though, just a bad workaround.
I also changed a few times the node / npm version, without success. Here's the package.json
{
"name": "askalfred-app",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"generate": "graphql-codegen",
"postinstall": "npm run build",
"start": "node server.js"
},
"dependencies": {
"#sentry/browser": "^5.4.0",
"#sentry/integrations": "^5.4.0",
"actioncable": "^5.2.2-1",
"apollo-boost": "^0.1.20",
"apollo-cache-inmemory": "^1.3.5",
"apollo-client": "^2.4.2",
"apollo-link": "^1.2.3",
"apollo-link-http": "^1.5.5",
"apollo-upload-client": "^10.0.1",
"autosize": "^4.0.2",
"epic-spinners": "^1.1.0",
"flexboxgrid": "^6.3.1",
"graphql": "^14.1",
"graphql-ruby-client": "^1.6.3",
"lowdb": "^1.0.0",
"mkdirp": "^0.5.1",
"moment": "^2.24.0",
"shortid": "^2.2.8",
"showdown": "^1.9.0",
"tingle.js": "^0.14.0",
"typescript": "^3.4.5",
"v-mask": "^1.3.3",
"vue": "^2.6.10",
"vue-analytics": "^5.17.0",
"vue-apollo": "3.0.0-beta.25",
"vue-notification": "^1.3.16",
"vue-router": "^3.0.1",
"vuelidate": "^0.7.4",
"vuex": "^3.0.1"
},
"devDependencies": {
"#graphql-codegen/cli": "^1.2.0",
"#graphql-codegen/fragment-matcher": "^1.2.0",
"#graphql-codegen/typescript": "^1.2.0",
"#vue/cli-plugin-babel": "^3.4.0",
"#vue/cli-plugin-eslint": "^3.5.1",
"#vue/cli-service": "^3.4.0",
"eslint": "^5.15.3",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.2.2",
"express": "^4.17.1",
"graphql-tag": "^2.9.0",
"node-sass": "^4.11.0",
"sass-loader": "^7.1.0",
"serve-static": "^1.14.1",
"vue-cli-plugin-apollo": "^0.20.0",
"vue-template-compiler": "^2.5.21"
},
"engines": {
"node": "v11.11.0",
"npm": "6.7.0"
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
Any idea what I could be doing wrong?
Somewhat working solution
For anyone which would end up in the same situation, I believe most tutorial about Dokku / VueJS are out of date, and lead to stupid mistakes, or at the very least does not tackle the deployment correctly with the newer versions. After several hours I figured:
"postinstall": "npm run build",
I removed this from the package.json and now it deploys without problem. I'm unsure if the success was due to removing the /dist from .gitignore in my project and building it locally, or if the something happens while it's deployed, but it's enough in my case.
Better way imo.
Just create an empty file called exactly ".static" on the root folder of the project.
Then on the dokku server run:
dokku config:set yourappname NGINX_ROOT=dist BUILDPACK_URL=https://github.com/dokku/buildpack-nginx
The dist folder needs to be commited (remove it from .gitignore).
Then run "git push dokku master".
I have an ionic 3 app.
On a page I have a form with some fields.
<form>
<ion-item>
<ion-label>First item</ion-label>
<ion-input type="text" [(ngModel)]="title" name="title"></ion-input>
</ion-item>
... some more simple fields ...
<ion-item>
<ion-label>Item below keyboard region</ion-label>
<ion-textarea [(ngModel)]="description" name="description"></ion-textarea>
</ion-item>
<button ion-button type="submit" block>Add Todo</button>
</form>
When I tab the first, the keyboard is shown and the input item is properly focussed, that is: shows a blinking caret.
Though, when I click a field at a position below the area needed to show the keyboard, I do not get a caret, although the field is actually focussed. When I type, the karakters are put in the field.
The main difference is that when clicking on the lower field, the form is shifted upwards when the keyboard shows.
How to fix this?
I am running the app on an iPad 2017, iOS 11.2.2.
package.json:
{
"name": "my app",
"version": "1.0.1",
"author": "Ionic Framework",
"homepage": "http://ionicframework.com/",
"private": true,
"scripts": {
"clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build --release",
"lint": "ionic-app-scripts lint",
"ionic:build": "ionic-app-scripts build",
"ionic:serve": "ionic-app-scripts serve"
},
"dependencies": {
"#angular/animations": "5.0.0",
"#angular/common": "5.0.0",
"#angular/compiler": "5.0.0",
"#angular/compiler-cli": "5.0.0",
"#angular/core": "5.0.0",
"#angular/forms": "5.0.0",
"#angular/http": "5.0.0",
"#angular/platform-browser": "5.0.0",
"#angular/platform-browser-dynamic": "5.0.0",
"#ionic-native/app-version": "^4.5.2",
"#ionic-native/calendar": "^4.3.2",
"#ionic-native/call-number": "^4.4.2",
"#ionic-native/camera": "^4.3.2",
"#ionic-native/core": "4.3.0",
"#ionic-native/date-picker": "^4.4.2",
"#ionic-native/file": "^4.4.2",
"#ionic-native/in-app-browser": "^4.3.3",
"#ionic-native/keyboard": "^4.4.2",
"#ionic-native/media-capture": "^4.4.0",
"#ionic-native/native-page-transitions": "^4.3.2",
"#ionic-native/splash-screen": "4.3.0",
"#ionic-native/status-bar": "4.3.0",
"#ionic/pro": "^1.0.9",
"#ionic/storage": "2.0.1",
"#ngx-translate/core": "^9.1.0",
"#ngx-translate/http-loader": "^2.0.1",
"call-number": "^1.0.1",
"com.telerik.plugins.nativepagetransitions": "^0.6.5",
"cordova-ios": "^4.5.4",
"cordova-plugin-app-version": "^0.1.9",
"cordova-plugin-calendar": "^4.6.0",
"cordova-plugin-camera": "^2.4.1",
"cordova-plugin-compat": "^1.2.0",
"cordova-plugin-datepicker": "^0.9.3",
"cordova-plugin-device": "^1.1.7",
"cordova-plugin-file": "^5.0.0",
"cordova-plugin-file-transfer": "^1.7.0",
"cordova-plugin-inappbrowser": "^1.7.2",
"cordova-plugin-ionic-webview": "^1.1.16",
"cordova-plugin-media-capture": "^1.4.3",
"cordova-plugin-privacyscreen": "^0.4.0",
"cordova-plugin-splashscreen": "^4.1.0",
"cordova-plugin-statusbar": "^2.3.0",
"cordova-plugin-whitelist": "^1.3.3",
"cordova-windows": "^5.0.0",
"intl": "^1.2.5",
"ionic-angular": "3.9.2",
"ionic-plugin-keyboard": "^2.2.1",
"ionicons": "3.0.0",
"mx.ferreyra.callnumber": "0.0.2",
"ng2-datepicker": "^2.2.1",
"plist": "^2.1.0",
"rxjs": "5.5.2",
"sw-toolbox": "3.6.0",
"zone.js": "0.8.18"
},
"devDependencies": {
"#ionic/app-scripts": "3.1.0",
"cors": "^2.8.4",
"typescript": "2.4.2",
"ws": "3.3.2"
},
"description": "An Ionic project",
"cordova": {
"plugins": {
"com.telerik.plugins.nativepagetransitions": {},
"cordova-plugin-camera": {
"CAMERA_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-calendar": {
"CALENDAR_USAGE_DESCRIPTION": "This app uses your calendar to plan sessions."
},
"cordova-plugin-privacyscreen": {},
"ionic-plugin-keyboard": {},
"cordova-plugin-whitelist": {},
"cordova-plugin-device": {},
"cordova-plugin-splashscreen": {},
"cordova-plugin-ionic-webview": {},
"cordova-plugin-inappbrowser": {},
"cordova-plugin-media-capture": {
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " ",
"PHOTOLIBRARY_USAGE_DESCRIPTION": " "
},
"cordova-plugin-datepicker": {},
"mx.ferreyra.callnumber": {},
"cordova-plugin-statusbar": {},
"call-number": {},
"cordova-plugin-file": {
"PHOTOLIBRARY_USAGE_DESCRIPTION": "This allows",
"PHOTOLIBRARY_ADD_USAGE_DESCRIPTION": "This allows",
"FILE_USAGE_DESCRIPTION": "This app uses your files to upload on sessions.",
"CAMERA_USAGE_DESCRIPTION": " ",
"MICROPHONE_USAGE_DESCRIPTION": " "
},
"cordova-plugin-app-version": {}
},
"platforms": [
"windows",
"ios"
]
}
}
app.module.ts:
...
imports: [
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
IonicModule.forRoot(MyApp, {scrollAssist: false, autoFocusAssist: 'delay'})
],
in app.component.ts:
this.platform.ready().then(() => {
console.log('Platform is ready!');
this.keyboard.disableScroll(false);
...
thanks!
I have this hackish solution, but I guess there have to be a better one. But because of the comment of user2158259, I'll post it anyway. Please don't punish me for posting it ;-)
1) Remove packages/plugins if you have them:
ionic cordova plugin remove ionic-plugin-keyboard
npm uninstall ionic-plugin-keyboard --save
npm uninstall #ionic-native/keyboard --save
2) Remove any references in app.module.ts such as
this.keyboard.disableScroll(true);
3) create a sub-folder in the app-folder and add these two files:
device.service.ts
The following code is a subset of my device.service.ts
import {ApplicationRef, EventEmitter, Injectable} from '#angular/core';
#Injectable()
export class DeviceService {
public onTick: EventEmitter<string> = new EventEmitter();
private tickValue = new Date().getTime();
constructor(public appRef: ApplicationRef) {
window.addEventListener('onresize', () => {
console.log('DeviceService: on resize');
this.doTick();
setTimeout(() => {
this.doTick();
}, 100);
}, false);
window.addEventListener('transitionend', () => {
console.log('transition ended');
this.doTick();
}, false);
this.tickValue = new Date().getTime();
setTimeout(() => {
this.doTick();
});
}
/**
* getTickValue() returns a different value when something changed (orientation, keyboard up/down).
* by adding this to the screen, the Ionic caret will be adjusted properly
*/
getTickValue(): string {
return this.tickValue + ' ' + window.innerWidth + ' ' + window.innerHeight + ' ' + window.orientation;
}
doTick(): void {
this.tickValue = new Date().getTime();
this.onTick.emit(String(this.tickValue));
this.appRef.tick();
}
}
kb-scroll.ts
import {ApplicationRef, Directive, ElementRef, HostListener, Renderer2} from '#angular/core';
import {DeviceService} from './device.service';
#Directive({
selector: '[kb-scroll]' // Attribute selector
})
export class KbScrollDirective {
constructor(public appRef: ApplicationRef,
public elm: ElementRef,
public renderer: Renderer2,
public device: DeviceService) {
}
#HostListener('click', ['$event'])
onClick($event) {
let elmClickedY;
let scrollContent: HTMLElement;
if ('TEXTAREA' === this.elm.nativeElement.tagName) {
scrollContent = $event.toElement;
elmClickedY = $event.offsetY;
} else if ('ION-CONTENT' === this.elm.nativeElement.tagName) {
// calculate Y offset between click and top of scroll-content area
scrollContent = this.elm.nativeElement.querySelector('.scroll-content');
if (scrollContent) {
// $event.offsetY is most likely small offset in clicked input field in scroll content
// calculate the offsetY opposed to the container (div.scroll-content)
let clickScreenY = $event.toElement.getBoundingClientRect().top + $event.offsetY;
let scrollContentScreenY = scrollContent.getBoundingClientRect().top;
elmClickedY = clickScreenY - scrollContentScreenY;
} else {
console.warn('KbScrollDirective: could not find .scroll-content div in ', this.elm.nativeElement);
}
} else {
console.warn('KbScrollDirective: Can\'t handle ', this.elm.nativeElement.tagName);
}
//TODO: OK to 'RE-ASSIGN' window.onresize ?
window.onresize = () => {
if (scrollContent) {
setTimeout(() => {
let elmHeight = scrollContent.clientHeight;
if (elmClickedY > elmHeight) {
let toScroll = elmClickedY - elmHeight;
scrollContent.scrollTop += toScroll + 40;
this.device.doTick();
}
}, 100);
}
}
}
}
4) Add this service and directive to you module
5) Use the kb-scroll directive on your <ion-content> HTML tag:
<ion-content kb-scroll>
... your form here ...
</ion-content>
6) now comes the really dirty part (and it will become clear why we need the service).
It seems that the repaint of the Ionic caret is triggered when something changes on the screen, so we need to force that.
I added a <span> to my HTML template that holds the root <ion-nav> tag:
app.html
<!-- invisible ticker, needed to get Keyboard caret into place -->
<div style="position: absolute; top:-100px">{{tickValue}}</div>
<!-- Disable swipe-to-go-back because it's poor UX to combine STGB with side menus -->
<ion-nav [root]="rootPage" #content swipeBackEnabled="false" class="root-content"></ion-nav>
7) Add this code in your app.component.ts:
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
...
tickValue: string;
constructor(...,
public device: DeviceService) {
this.device.onTick.subscribe(tickValue => {
this.tickValue = tickValue;
});
}
So whenever the DeviceService#tickValue get's updated, this subscriber will update the app component's tick-value, causing a repaint of the screen, although the tick value is rendered outside the visible area of the screen. This seems to cause the Ionic caret to be positioned correctly.
Bonus: this also works for a textarea that takes more place than half the screen (= screen height - keyboard height). Just add the kb-scroll directive to your <textarea> HTML tag.
Please note this is a very hackish solution.
Any comments/improvements are really welcome!
There is white screen displaying after splash screen for few seconds in nativescript ios. It has been happening only for our project but sample project is working fine. I checked both simulator and iPhone 5, iPhone 6 , iPad mini ,white screen is displaying in all devices. I am using Nativescript with Angular 4, Here I included my package.json
{
"description": "NativeScript Application",
"license": "SEE LICENSE IN <your-license-filename>",
"readme": "NativeScript Application",
"repository": "<fill-your-repository-here>",
"nativescript": {
"id": "com.preludesys.calladoc",
"tns-ios": {
"version": "3.1.0"
}
},
"scripts": {
"tslint": "tslint -p tsconfig.json",
"ns-bundle": "ns-bundle",
"publish-ios-bundle": "npm run ns-bundle --ios --publish-app",
"generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install",
"start-android-bundle": "npm run ns-bundle --android --run-app",
"start-ios-bundle": "npm run ns-bundle --ios --run-app",
"build-android-bundle": "npm run ns-bundle --android --build-app",
"build-ios-bundle": "npm run ns-bundle --ios --build-app"
},
"dependencies": {
"#angular/animations": "~4.1.0",
"#angular/common": "~4.1.0",
"#angular/compiler": "~4.1.0",
"#angular/core": "~4.1.0",
"#angular/forms": "~4.1.0",
"#angular/http": "~4.1.0",
"#angular/platform-browser": "~4.1.0",
"#angular/router": "~4.1.0",
"nativescript": "^3.2.0",
"nativescript-angular": "~3.0.0",
"nativescript-camera": "^3.0.1",
"nativescript-drop-down": "^3.1.0",
"nativescript-google-maps-sdk": "^2.3.2",
"nativescript-imagepicker": "^3.0.6",
"nativescript-iqkeyboardmanager": "^1.1.0",
"nativescript-loading-indicator": "^2.3.2",
"nativescript-phone": "^1.3.1",
"nativescript-ripple": "^2.0.0",
"nativescript-telerik-ui": "^3.0.4",
"nativescript-theme-core": "~1.0.2",
"nativescript-unit-test-runner": "^0.3.4",
"nativescript-xml2js": "^0.5.2",
"reflect-metadata": "~0.1.8",
"rxjs": "~5.3.0",
"tns-core-modules": "^3.1.1",
"zone.js": "~0.8.2"
},
"devDependencies": {
"#angular/compiler-cli": "~4.1.0",
"#ngtools/webpack": "^1.3.0",
"babel-traverse": "6.24.1",
"babel-types": "6.24.1",
"babylon": "6.17.1",
"codelyzer": "^3.0.1",
"copy-webpack-plugin": "~4.0.1",
"extract-text-webpack-plugin": "~2.1.0",
"filewalker": "^0.1.3",
"jasmine-core": "^2.6.2",
"karma": "^1.7.0",
"karma-jasmine": "^1.0.2",
"karma-nativescript-launcher": "^0.4.0",
"lazy": "1.0.11",
"nativescript-css-loader": "~0.26.0",
"nativescript-dev-typescript": "~0.4.0",
"nativescript-dev-webpack": "next",
"raw-loader": "~0.5.1",
"resolve-url-loader": "~2.0.2",
"tslint": "^5.4.2",
"typescript": "~2.2.1",
"webpack": "~2.6.1",
"webpack-bundle-analyzer": "^2.8.2",
"webpack-sources": "~1.0.1",
"nativescript-worker-loader": "~0.8.1"
}
}
i dont know what is the issue and how can i fix it. so please j=help me.
You should repeat the layout of your boot screen or a slightly different version of it (with a spinner and loading... message) hard coded that gets removed or drawn over once the app is actually loaded. This will give a smooth and professional looking transition between the boot screen and the loaded application.
This short "splash after the splashscreen" appears on iOS during the load of the initial page.
You cannot avoid it. However, you can change the color in your global CSS file with this code:
Page {
background-color: black;
}
In our apps this white screen becomes more prominent after adding a few "heavy" plugins, as nativescript-telerik-ui, nativescript-google-maps-sdk, etc.
It happens on old (slow) Android devices, too.
It seems it is unavoidable, which is a shame, really. One thing you could try is to optimize the loading time with Webpack, lazy loading, etc., so the app loads faster.
It also is recommended to change the Page background color programmatically on boot.
I am using frames and i found solution to make Frame background same as Splash screen
Frame {
background:#4285F4;
}
For this to work on Android {N}7 too, do:
frame.ns-root {
background-color: black;
}