I get an issue when I focus a TextView at the bottom of my app.
When I tap the TextView the Keyboard appears, but on iOS the whole view doesn't shift to top.
As you can see the android has the expected behavior but on iOS the keyboard just overlays the other whole page.
My main-page.xml:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="onNavigatingTo">
<GridLayout rows="*,auto">
<TextView text="Lorem ipsum......" />
<TextView col="0" row="1" backgroundColor="green" textWrap="true" hint="type text here"/>
</GridLayout>
</Page>
I am using newest tons version and iOS/Android runtimes.
I don't do a lot of iOS dev, but I'm fairly certain that's just the default native behavior for iOS. Android has a property you can set in the manifest to enable the view adjustments when the soft keyboard shows and it's enabled by default with NS apps. On iOS a lot of people use this, https://www.npmjs.com/package/nativescript-iqkeyboardmanager plugin which wraps the native IQKeyboardManager library for iOS that does some handling of the view adjustments for soft keyboard opening.
And the problem with that plugin (nativescript-iqkeyboardmanager ) is that it pushes the action bar to the top making it not visible, it pushes up the whole page, I had to add a hacky way by calculating the keyboard size and resizing a bunch of stuff on TextEditing.....
@Daxito I have same issue as you. Could you maybe explain/show what you did? 馃樅
@mikkeldamm It was very specific to my needs, because it was a Chat Screen with one single Texbox always at the bottom, so just by listening changes in the height of the keyboard like this :
application.ios.addNotificationObserver(UIKeyboardWillChangeFrameNotification, function (notification) {
let hght = notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
})
Then I would just add a dummy Stack Panel underneath the textbox and when user started typing then I would set the height of this Stack Panel equals to the Notified Keyboard height thus pushing the TextBox up.... it worked :-)
If you have a "form like" screen, then it gets complicated...
@Daxito Great, its the exact same scenario that I have. Thanks 馃憤
I have a compiling error that says: Cannot find name 'UIKeyboardFrameEndUserInfoKey'. What should I add in order to run the code properly?
@viniciodeltoro because these names are available in the native apps, then we can use them, but they are not available for us via typescript, since those types doesn't exists anywhere.
You therefor need to declare them, and it can be done by adding this to any typescript file (I have added it to my reference.d.ts
)
declare var UIKeyboardFrameEndUserInfoKey: string;
I usually prefer to use specific types over any, but you could also write any:
declare var UIKeyboardFrameEndUserInfoKey: any;
Ohh, and for the solution above created by @Daxito, then I ended up creating a observer for the height change of the keyboard. It looks like this:
export class KeyboardObserver {
public heightChange$(): Observable<number> {
return Observable.create((observer) => {
const iosObserver = application.ios.addNotificationObserver(UIKeyboardWillChangeFrameNotification, (notification) => {
const height = notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
observer.next(height);
});
return () => {
application.ios.removeNotificationObserver(iosObserver, UIKeyboardWillChangeFrameNotification);
}
});
}
}
Then I can inject the class in any component and subscribe like this:
this._keyboardObserver.heightChange$().subscribe((height) => {
console.log(height);
});
Thanks for your help @mikkeldamm. I want to mention something different that I had to do in my code in order to get the height. I had to use 'CGRectValue()' instead of 'CGRectValue'. iOS was throwing an error and the app was getting closed. So, my full function looks like this:
private setResizeScrollIOS(){
if (application.ios) {
application.ios.addNotificationObserver(UIKeyboardWillChangeFrameNotification, (notification) => {
let height:any = notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue().size.height;
console.log(height);
this.keyboardGap = height;
});
}
}
It works now, thank you.
@Daxito Thanks for the hack. Can you tell me how you dealt with the stack panel when the keyboard is down. Because I need to set the height of the stack panel as zero, once the keyboard is down. The nativescript-iqkeyboardmanager plugin don't provide the event when done button is pressed. Thanks in advance.
I use a delegate for the textbox the has the focus (attached
myUITextViewDelegateImpl.txt
)
And I use it like this:
var txtMessage = page.getViewById('txtMessage');
if (txtMessage.ios) {
let delegates = require("~/utils/myUITextViewDelegateImpl");
_myDelegate = delegates.myUITextViewDelegateImpl.initWithOwner({
owner: new WeakRef(txtMessage),
onTextViewShouldBeginEditing: () => {
page.getViewById("StackPanelID").height = TheCurrentKeyBoardHeight
},
onTextViewDidEndEditing: () => {
page.getViewById("StackPanelID").height = 0;
}
});
txtMessage.ios.delegate = _myDelegate;
}
@mikkeldamm Now in NS 4.2, I couldn't figure out why I get the same height (non-zero) when the keyboard opens and closes, so i figured out that the function returns the keyboard height at that exact moment.
I then decided to use UIKeyboardWillShowNotification
and UIKeyboardWillHideNotification
.
In both cases the expression:
notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
returns the same value, but I now know what the height should be.
(all constants are listed in apple docs here)
Hi all,
I've tried @mikkeldamm solution, but it doesn't work with UIKeyboardWillShowNotification. I do not get the notification. However, I do get UIKeyboardDidShowNotification. But this comes "too late" so the textfield is rendered after I already see the keyboard. Anyone has the same problems and knows how to fix it?
Silly question but:
I get:
error TS2304: Cannot find name 'UIKeyboardWillChangeFrameNotification'
Any ideas?
@dolanmiu did you declare var UIKeyboardWillChangeFrameNotification;
before your @Component
definition?
I found a better way:
Installing and using tns-platform-declarations
fixed it
Anyone experiencing the height changing after you start typing?
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.0ed6a3a3de7a937ddad3.hot-update.js:58:28: 291
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.0ed6a3a3de7a937ddad3.hot-update.js:64:20: focus
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.js:609:28: 335
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.0ed6a3a3de7a937ddad3.hot-update.js:58:28: 291
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.js:609:28: 335
LOG from device Yavor鈥檚 iPhone: CONSOLE LOG file:///app/bundle.0ed6a3a3de7a937ddad3.hot-update.js:58:28: 335
@YavorIvanov Try to place the height change in a setTimeout() or ngZone.run()
I created a service around the Observable example above and it reports right away if I delay it in any way it still reports the first "wrong" value.
```import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import * as applicationModule from "tns-core-modules/application";
declare var UIKeyboardFrameEndUserInfoKey: any;
declare var UIKeyboardWillChangeFrameNotification: any;
@Injectable({
providedIn: 'root'
})
export class KeyboardService {
constructor() { }
public heightChange$(): Observable
return Observable.create((observer: any) => {
const iosObserver = applicationModule.ios.addNotificationObserver(UIKeyboardWillChangeFrameNotification, (notification) => {
const safeArea = applicationModule.ios.window.safeAreaInsets.bottom;
const height = notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
observer.next(height - safeArea);
});
return () => {
applicationModule.ios.removeNotificationObserver(iosObserver, UIKeyboardWillChangeFrameNotification);
}
});
}
}
```
Doing this actually alleviates the issues with the 30 or so points initial wrong focus.
But I really dislike this hack.
onFocus(args) {
console.log('focus');
args.object.animate({ // help to resize keyboard
translate: { x: 0, y: 0 },
duration: 10,
delay: 0
}).
then(() => {
args.object.animate({ // help to resize keyboard
translate: { x: 0, y: 0 },
duration: 10,
delay: 0
})
})
}
Hello guys, i'm trying to do the solution presented by @viniciodeltoro, but when i click the textview to insert the text, the app crashes and show this message 'notification.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue' is an instance of CGRect, and i don't know what to do ! :/
Most helpful comment
Ohh, and for the solution above created by @Daxito, then I ended up creating a observer for the height change of the keyboard. It looks like this:
Then I can inject the class in any component and subscribe like this: