Ionic View - Status bar overlap [duplicate] - ionic-framework

How do I avoid ionic header from getting behind ios status bar like this?
I created the header with following code:
<ion-view title="{{title}}" hide-nav-bar="false">

From: http://www.sitepoint.com/5-ionic-app-development-tips-tricks/
Ionic has specific classes to deal with this – platform-ios and platform-cordova. Ionic’s pre-built elements target those classes and add in 20px top margin to the buttons in your header to make up for these situations. To get my own custom elements to do the same, I follow the same pattern. For my .search-bar above, I add a margin if we’ve got a .platform-ios.platform-cordova:not(.fullscreen) body class. Example:
.platform-ios.platform-cordova:not(.fullscreen) .search-bar {
margin-top: 20px;
}
This should be the correct answer.

I have been stuck on the same problem for a long time and none of the above solutions worked for me. Finally, to get the status bar from overlapping on the app's view, I added the following preference to my config.xml file and viola:
<preference name="StatusBarOverlaysWebView" value="false" />
<preference name="StatusBarBackgroundColor" value="#000000" />
<preference name="StatusBarStyle" value="lightcontent" />
This ofcourse requires the plugin: cordova-plugin-statusbar "StatusBar".
Hope this helps.

The solution with StatusBar.styleDefault(); did not work for me, but calling StatusBar.overlaysWebView(false) did the job.
First screenshot is UI of the app before calling StatusBar.overlaysWebView(false), you can see that the iOS status bar is over the app and user cannot click the whole area of menu button:
Now when I call StatusBar.overlaysWebView(false), the iOS status bar is no longer over the menu button:

Finally the problem is solved.
in app.js
$ionicPlatform.ready(function() {
if (window.cordova && $cordovaKeyboard) {
$cordovaKeyboard.hideAccessoryBar(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
}
and, if that doesn't solve the problem yet. In index.html, cordova.js should be imported on the very last.
<head>
...
<script src="cordova.js"></script>
</head>
This solve my problem.

adding margin won't work exactly. It will only create an empty space at the bottom of your screen.
The fix is to make sure you have viewport-fit = cover in your app's meta tag.

Try this for Ionic 5.x, just put index.html inside
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />

This code is working for me with ionic 3 (3.7.1)
Open App.scss in app folder then add this code:
.platform-ios
{
page-home { // replaced by your page HERE
top: 20px !important;
}
}

Just a quick reminder,
If this issue only happens in some of the subpages, you probably used nav.push() inside a modal(this is depreciated).
To fix this issue, in your modal
import { App, ViewController } from 'ionic-angular';
constructor(
public viewCtrl: ViewController
public app: App
) {}
replace push() with below
//this.navCtrl.push(nextPage);
this.viewCtrl.dismiss();
this.appCtrl.getRootNav().push(nextPage);

I found a better option to do this. In AppModule.ts make change to IonicModule.forRoot(MyApp, {statusbarPadding: true}), and from app.component.ts remove or comment statusBar.overlaysWebView(true); it will fix your problem for IOS.

Add following code in config.xml
<preference name="SplashShowOnlyFirstTime" value="false" /><br>
<preference name="SplashScreen" value="screen" /><br>
<preference name="SplashScreenDelay" value="3000" /><br>
App.component.ts<br>
Inside constructor<br>
this.statusBar.overlaysWebView(false);<br>
this.statusBar.styleLightContent()

#Ferdy-Fauzi's answer did the trick; however, since the index.html gets overwritten on every build here my usage of ionic hooks to apply the changes after every build.
Add hooks in your ionic.config.json file
{
"name": "xxxx",
"integrations": {
"capacitor": {}
},
"type": "vue",
"id": "xxxxx",
"hooks": {
"build:after": "./deploy/after-build.js"
}
}
and then in your project folder (top level) create folder deploy with file after-build.js with following content:
var fs = require('fs')
module.exports = (ctx) => {
const index = `${ctx.project.dir}/dist/index.html`;
fs.readFile(index , 'utf8', (err, html) => {
const meta = '<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />';
html = html.replace(/<meta name="viewport".*?">/, meta);
fs.writeFile(index, html, 'utf8', err => err && console.log( err ));
});
};

Related

React-Admin CSS theme overrides are placed earlier in <head> and getting overwritten by MaterialUI

After upgrading to React-Admin 3.15 (and #material-ui/core 4.11.4 (styles as well)) we're running into some issues with styles. For example - for our sidebar <MenuItemLink /> elements we've overridden the colors in the global theme, as recommended by docs. They technically work, they are inserted into the HTML head, but are placed before basic MaterialUI styles (like .MuiButtonBase-root) and those overwrite them.
React-Admin's styles are inserted in the middle of head
This wasn't the issue prior to the upgrade (we used react-admin#3.10.3 and #material-ui/core#4.11.1).
Previously, RA styles were inserted at the end of head
I'm not really sure what code is relevant and we're kind of suspecting Next.js as well, so here's pages/_app.tsx and pages/_document.tsx (these files have not been changed during the update):
pages/_app.tsx
const App = ({ Component, pageProps }: AppProps) => {
// remove server-side generated CSS once it's rendered, so it doesn't collide with styles generated on the client
useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side')
if (jssStyles) {
jssStyles.parentElement?.removeChild(jssStyles)
}
}, [])
return (
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>
)
}
App.getInitialProps = async (ctx: NextUrqlAppContext) => {
const appProps = await NextApp.getInitialProps(ctx)
return { ...appProps }
}
pages/_document.tsx
// is processed server-side only
export default class MyDocument extends Document {
render(): JSX.Element {
return (
<Html lang="en">
<Head>
<meta name="theme-color" content={theme.palette.primary.main} />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400,500,700&display=swap" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto+Slab:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
// collects server-side generated styles, and saves them to head under #jss-server-side I assume
MyDocument.getInitialProps = async (ctx) => {
const sheets = new ServerStyleSheets()
const originalRenderPage = ctx.renderPage
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement()
],
}
}
The theme we're using is created in a file, exported and imported in the React-Admin's <Admin /> component:
const theme = {
//...
overrides: {
RaMenuItemLink: {
root: {
color: 'rgba(255, 255, 255, 1)',
'&:hover': {
backgroundColor: green[900],
color: 'rgba(255, 255, 255, 1)',
}
},
active: {
backgroundColor: green[500],
color: 'rgba(255, 255, 255, 1)',
}
}
}
}
I also tried removing the .next folder, thinking maybe it's a cache issue, and hard-reload in browser too, to no avail.
EDIT: The styles seems even further inconsistent - e.g. two <BooleanInputs> with identical code (on different pages), yet one of them has .MuiIconButton-root overshadowing .MuiSwitch-switchBase, effectively turning off the transition: left... CSS property, while the other works fine. That happens because for some reason, .MuiIconButton-root is inserted into <head> twice, overwriting that property.
If you happen to stumble on the same problem as me - before trying anything else, do yourself a favor and try deleting the entire node_modules and reinstall packages if you can.
I thought I matched the #material-ui versions and was almost sure the problem is somewhere else and after 3 days of asking and trying to bandaid fix problems I randomly thought that maybe something really broke during the upgrade and well... doesn't hurt my chances to reinstall fresh. And it actually solved my issue.

Is there any way to import an image to my materialui-nextjs project

I imported the image like this :
import img from '../public/buildingspattern.png'
then used it in the component :
<Card className={classes.root}>
<CardMedia
className={classes.media}
image={img}
title="Buildings"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
Something
</Typography>
</CardContent>
</Card>
then I fixed my next.config.js
const webpack = require('webpack');
module.exports = {
i18n: {
locales: ["en", "fr"],
defaultLocale: "en",
},
webpack: (config, options) => {
config.module.rules.push({
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
})
return config
},
};
I think I miss something here
The App starts fine, But the image isn`t there.
That's what I see in the webpage after I yarn dev
<div class="MuiCardMedia-root
makeStyles-media-16"
style="background-
image:url("8a955c62dc9b08f560cb800b273932ac.png")"
title="Buidings">
</div>
I came across another way as I just implemented it today..
Which is simpler and more efficient,
Go to the Card component in MaterialUi
Replace the standard <img/> tag with <Image /> tag
Also please add import Image from 'next/image'
Suppose you create a component for returning Image
import Image from 'next/image';
function ImageComponent() {
return (
<Image src="../public/someImage.png" alt="Buildings Pattern"/>
);
};
And use this component in your image attribute using interpolation as you did above
image={ImageComponent}
✌
First of all, You can use the absolute path of the image in next.js like this /image-name, If don't work you will create a directory in the public directory as name static and put your image in the static directory then you can access /static/image name

Getting a dialog on click of Edit Button on admin-on-rest

I am working on an application using admin-on-rest framework. For editing an entry on a Resource we provide XXXEdit, XXXShow, XXXCreate props to it. My requirement is that when I click on an Edit button in List view on any entry I should get a Dialog box with the parameters in XXXEdit instead of going to a new page. I tried doing this by using a Dialog in XXXEdit component
<Edit title={<RoleTitle />} {...props}>
<SimpleForm>
<Dialog
title="Dialog With Actions"
actions={actions}
modal={false}
open={true}
>
<TextInput source="id" />
<TextInput source="name" validate={required} />
.
.//some more fields
</Dialog>
</SimpleForm>
</Edit>
I get errors like The TextInput component wasn't called within a redux-form
If I use a DisabledInput then I get an error cannot read value of undefined
How do I go on with this?
I do not think you can use Simpleform for this. You will need to create a custom Form using Redux-Form. Look at the bottom answer that documents the final answer.
This might help you
How to richly style AOR Edit page
Instead of creating a page. You are creating a component that connects to the Redux state and displays as a dialog box.
I tried to resolve this using HOC and react-router.
I created a button using AOR button and provided a containerElement
containerElement={
<Link
key={record.id}
to={{
...{
pathname: `${basePath}/${encodeURIComponent(record.id)}`
},
...{ state: { modal: true } }
}}
/>
}
I created a route like this where DialogRoleEdit is an AOR edit component wrapped with a dialog HOC below .
<Route
exact
path="/roles/:id"
render={routeProps => {
return !!(
routeProps.location.state && routeProps.location.state.modal
) ? (
<Restricted authClient={authClient} location={routeProps.location}>
<div>
<RoleList resource={"roles"} {...routeProps} />
<DialogRoleEdit resource={"roles"} {...routeProps} />
</div>
</Restricted>
) : (
<Restricted authClient={authClient} location={routeProps.location}>
<RoleEdit resource={"roles"} {...routeProps} />
</Restricted>
);
}}
/>
Finally an HOC
handleClose = () => {
this.props.history.goBack();
};
render() {
const actions = [
<FlatButton label="Cancel" primary={true} onClick={this.handleClose} />
];
return (
<Dialog>
<WrappedComponent/>
</Dialog>
)
}
We need to provide edit prop for this resource in App.js
edit={DialogUserEdit}

How to implement OAuth.io using Ionic Framework for LinkedIn?

I have created the LinkedIn app and retrieved the client id and client_secret.
Now inside the integrated api of OAuth.io created an api and have added the keys and permission scope.
I want to run this project using Ionic Framework. What should be done to achieve it.
P.S: I am new to Ionic Framework and OAuth.io. So please don't mind my style of asking the question.
whole index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script src="lib/ionic/js/ionic.bundle.js"></script>
<script src="js/ng-cordova.min.js"></script>
<script src="js/ng-cordova-oauth.min.js"></script>
<script src="cordova.js"></script>
<script src="js/app.js"></script>
</head>
<body ng-controller="MainCtrl">
<button class="button" ng-click="linkedInLogin()">Login via LinkedIn</button>
</body>
</html>
whole app.js:
angular.module('starter', ['ionic', 'ngCordova', 'ngCordovaOauth'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
.controller("MainCtrl", function($scope, $cordovaOauth) {
document.addEventListener( "deviceready", onDeviceReady );
function onDeviceReady( event ) {
// on button click code
$scope.linkedInLogin = function(){
OAuth.initialize('07IxSBnzVoGGQL2MpvXjSYakagE')
OAuth.popup('linkedin').done(function(result) {
// Here you will get access token
console.log(result)
result.me().done(function(data) {
// Here you will get basic profile details of user
console.log(data);
})
});
};
}
});
Please go through the steps and below code:
1) create a project from terminal as ionic start linkedinlogin blank
2)cd linkedinlogin project
3)Add the required platform in terminal as ionic add platform ****
4)Add the ng-cordova.min.js file above the cordova.ja file in our project
5)Install ng-cordova-oauth as bower install ng-cordova-oauth -S
6)Then include ng-cordova-oauth.min.js file in index.html
7)Inject 'ngCordova' and 'ngCordovaOauth' as dependency in app.js file
8)In index.html create a button as login via linkedin
9)In app.js create a Controller with below code
10)Please update your cordova platform if the above plugin doesn't work
$cordovaOauth.linkedin(clientId, clientSecret, ['r_basicprofile', 'r_emailaddress']).then(function(success){
//Here you will get the access_token
console.log(JSON.stringify(success));
$http({method:"GET", url:"https://api.linkedin.com/v1/people/~:(email-address,first-name,last-name,picture-url)?format=json&oauth2_access_token="+success.access_token}).success(function(response){
// In response we will get firstname, lastname, emailaddress and picture url
// Note: If image is not uploaded in linkedIn account, we can't get image url
console.log(response);
}, function(error){
console.log(error);
})
}, function(error){
console.log(error);
})
I thing you read the ngCordova plugins.
Using oauth.io i have implemented login via linkedin:
Please follow the steps:
1. Create a app in oauth.io and get public key.
2. Click on the Integrated APIs menu from the left side bar.
3. Now click on ADD APIs green button on the right top corner.
4. Now Search and select LinkedIn.
5. Now add the Client id and Client Secret in keys and permission scope.
6. use below command to add plugin to project:
cordova plugin add https://github.com/oauth-io/oauth-phonegap
7. For controller code check below code.
document.addEventListener( "deviceready", onDeviceReady );
function onDeviceReady( event ) {
// on button click code
$scope.linkedInLogin = function(){
OAuth.initialize('your public key')
OAuth.popup('linkedin').done(function(result) {
// Here you will get access token
console.log(result)
result.me().done(function(data) {
// Here you will get basic profile details of user
console.log(data);
})
});
};
}
Hope it may be help you..

Ionic ion-view header behind ios status bar

How do I avoid ionic header from getting behind ios status bar like this?
I created the header with following code:
<ion-view title="{{title}}" hide-nav-bar="false">
From: http://www.sitepoint.com/5-ionic-app-development-tips-tricks/
Ionic has specific classes to deal with this – platform-ios and platform-cordova. Ionic’s pre-built elements target those classes and add in 20px top margin to the buttons in your header to make up for these situations. To get my own custom elements to do the same, I follow the same pattern. For my .search-bar above, I add a margin if we’ve got a .platform-ios.platform-cordova:not(.fullscreen) body class. Example:
.platform-ios.platform-cordova:not(.fullscreen) .search-bar {
margin-top: 20px;
}
This should be the correct answer.
I have been stuck on the same problem for a long time and none of the above solutions worked for me. Finally, to get the status bar from overlapping on the app's view, I added the following preference to my config.xml file and viola:
<preference name="StatusBarOverlaysWebView" value="false" />
<preference name="StatusBarBackgroundColor" value="#000000" />
<preference name="StatusBarStyle" value="lightcontent" />
This ofcourse requires the plugin: cordova-plugin-statusbar "StatusBar".
Hope this helps.
The solution with StatusBar.styleDefault(); did not work for me, but calling StatusBar.overlaysWebView(false) did the job.
First screenshot is UI of the app before calling StatusBar.overlaysWebView(false), you can see that the iOS status bar is over the app and user cannot click the whole area of menu button:
Now when I call StatusBar.overlaysWebView(false), the iOS status bar is no longer over the menu button:
Finally the problem is solved.
in app.js
$ionicPlatform.ready(function() {
if (window.cordova && $cordovaKeyboard) {
$cordovaKeyboard.hideAccessoryBar(true);
}
if (window.StatusBar) {
StatusBar.styleDefault();
}
}
and, if that doesn't solve the problem yet. In index.html, cordova.js should be imported on the very last.
<head>
...
<script src="cordova.js"></script>
</head>
This solve my problem.
adding margin won't work exactly. It will only create an empty space at the bottom of your screen.
The fix is to make sure you have viewport-fit = cover in your app's meta tag.
Try this for Ionic 5.x, just put index.html inside
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
This code is working for me with ionic 3 (3.7.1)
Open App.scss in app folder then add this code:
.platform-ios
{
page-home { // replaced by your page HERE
top: 20px !important;
}
}
Just a quick reminder,
If this issue only happens in some of the subpages, you probably used nav.push() inside a modal(this is depreciated).
To fix this issue, in your modal
import { App, ViewController } from 'ionic-angular';
constructor(
public viewCtrl: ViewController
public app: App
) {}
replace push() with below
//this.navCtrl.push(nextPage);
this.viewCtrl.dismiss();
this.appCtrl.getRootNav().push(nextPage);
I found a better option to do this. In AppModule.ts make change to IonicModule.forRoot(MyApp, {statusbarPadding: true}), and from app.component.ts remove or comment statusBar.overlaysWebView(true); it will fix your problem for IOS.
Add following code in config.xml
<preference name="SplashShowOnlyFirstTime" value="false" /><br>
<preference name="SplashScreen" value="screen" /><br>
<preference name="SplashScreenDelay" value="3000" /><br>
App.component.ts<br>
Inside constructor<br>
this.statusBar.overlaysWebView(false);<br>
this.statusBar.styleLightContent()
#Ferdy-Fauzi's answer did the trick; however, since the index.html gets overwritten on every build here my usage of ionic hooks to apply the changes after every build.
Add hooks in your ionic.config.json file
{
"name": "xxxx",
"integrations": {
"capacitor": {}
},
"type": "vue",
"id": "xxxxx",
"hooks": {
"build:after": "./deploy/after-build.js"
}
}
and then in your project folder (top level) create folder deploy with file after-build.js with following content:
var fs = require('fs')
module.exports = (ctx) => {
const index = `${ctx.project.dir}/dist/index.html`;
fs.readFile(index , 'utf8', (err, html) => {
const meta = '<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />';
html = html.replace(/<meta name="viewport".*?">/, meta);
fs.writeFile(index, html, 'utf8', err => err && console.log( err ));
});
};