iOS keyboard doesn't activate when TextField receives focus
-
Is there something special that needs to happen? Some property that needs to be set? Some Xcode project setting? This seems to occur on all TextField instances for me on both iPhone and iPad.
Thanks!
-
Hi @patrickkidd
Can you please tell me Which Qt Version you are using ?
As i use the
TextField {}
in my code it dose take the focus in iOS.- I am using
QtQuick.Controls 2.2
Can you check with below properties which might help you.
Below is example from my code:
import QtQuick.Controls 2.2 ` ` ` ` TextField { id: dataField placeholderText: qsTr("Please Enter Data") maximumLength: 18 // I restrict the input length. Layout.fillWidth: true // As i have it inside the ColumnLayout } ` ` ` ` Component.onCompleted: { // Do it for the root Component dataField.forceActiveFocus(); }
All the best.
- I am using
-
Hi @patrickkidd
if your QML is inside a QQuickWidget or a QQuickView, than that is a well known, but infixed bug
https://bugreports.qt.io/browse/QTBUG-63157
My, very hackish, workaround for iOS is to create an offscreen QLineEdit that I set the focus on manually and forward the textChanged signal to QML.
I hate it, but it works.
-
@J.Hilk said in iOS keyboard doesn't activate when TextField receives focus:
Hi @patrickkidd
if your QML is inside a QQuickWidget or a QQuickView, than that is a well known, but infixed bug
https://bugreports.qt.io/browse/QTBUG-63157
My, very hackish, workaround for iOS is to create an offscreen QLineEdit that I set the focus on manually and forward the textChanged signal to QML.
I hate it, but it works.
Do you have a code snippet? How do you detect the QML Item kb focus? Then how do you forward the kb events to the QML component?
-
@patrickkidd like I said, this code is nearly 2 years old, when I did my baby steps in QML x) so its convoluted
TextInput{ id:nameEdit anchors.fill: parent font.pixelSize: Manager.fontSize enabled: !root.reactToClicked activeFocusOnPress: true Keys.onReturnPressed:{ deselect() Qt.inputMethod.hide(); parent.forceActiveFocus() } horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter } MouseArea{ id:iosNameEdit anchors.fill: nameEdit visible: Qt.platform.os === "ios" && !root.reactToClicked property string uuid: Manager.newUuid() onClicked: { Manager.initLineEdit(nameEdit.text,iosNameEdit.uuid,false); root.moveUp() } Connections{ target: Manager onCurrentTextChanged:{ if(uuid === iosNameEdit.uuid ) { nameEdit.text = text } } onEditingFinished:{ if(uuid === iosNameEdit.uuid ) { root.moveDown() } } } }
The relevant part of Manager instance:
Q_INVOKABLE QString newUuid(){return QUuid::createUuid().toString();} Q_INVOKABLE void initLineEdit(const QString &text, const QString &uuid, bool useTimeRestiction) { if(iosLineEdit) iosLineEdit->initLineEdit(text,uuid,useTimeRestiction); } //Manager constructor iosLineEdit = new iOSLineEdit(qobject_cast<QWidget*>(parent)); iosLineEdit->show(); iosLineEdit->lower(); connect(iosLineEdit, &iOSLineEdit::currentTextChanged, this, &Manager::currentTextChanged); connect(iosLineEdit, &iOSLineEdit::currentEditFinsihed, this, &Manager::editingFinished); //iosLineEdit.h class QRegExpValidator; class iOSLineEdit : public QLineEdit { Q_OBJECT public: explicit iOSLineEdit(QWidget *parent = nullptr); public slots: void initLineEdit(const QString &text, const QString &uuid, bool useTimeRestiction); signals: void currentTextChanged(const QString &uuid, const QString &text); void currentEditFinsihed(const QString &uuid); protected: QString m_Uuid; QRegExpValidator *m_Validator; }; //iosLineEdit.cpp #include "ioslineedit.h" #include <QRegExpValidator> #include <QDebug> iOSLineEdit::iOSLineEdit(QWidget *parent) : QLineEdit (parent) { setMaximumSize(QSize(0,0)); connect(this, &QLineEdit::textChanged, this, [=](const QString &text)->void{ if( !m_Uuid.isEmpty()) { emit currentTextChanged(m_Uuid, text); } }); connect(this, &QLineEdit::editingFinished, this, [=]()->void{ if(!m_Uuid.isEmpty()) emit currentEditFinsihed(m_Uuid); m_Uuid.clear(); clear(); setEnabled(false); hide(); }); m_Validator = new QRegExpValidator(QRegExp("/^(?:(?:([0-9]?\d|2[0-3]):)?([0-5]?\d):)?([0-5]?\d)$/"),this); lower(); hide(); } void iOSLineEdit::initLineEdit(const QString &text, const QString &uuid, bool useTimeRestiction) { qDebug() << Q_FUNC_INFO; m_Uuid.clear(); if(useTimeRestiction) { setInputMask(QString("99:99:99")); setValidator(m_Validator); } else { setInputMask(QString()); setValidator(nullptr); } setText(text); m_Uuid = uuid; setEnabled(true); show(); setFocus(); }
-
@patrickkidd
Ignore the above post.I took the time to refine my original fix.
You can find a working GitHub example here:
https://github.com/DeiVadder/IosTextFieldWorkaround -
@J.Hilk said in iOS keyboard doesn't activate when TextField receives focus:
@patrickkidd
Ignore the above post.I took the time to refine my original fix.
You can find a working GitHub example here:
https://github.com/DeiVadder/IosTextFieldWorkaroundI tried this concept out in my app and it does work. Of course, there is quite a lot of features that need to be added, for example showing cursor position, allowing the selection of text, things like that.
Let's hope this bug gets fixed.
-
@patrickkidd I think, once the keyboard is up, you could transfer the focus back to the textfield and the keyboard input would actually go to the textfield.
But I haven't tested that thoroughly
-
@J.Hilk said in iOS keyboard doesn't activate when TextField receives focus:
@patrickkidd I think, once the keyboard is up, you could transfer the focus back to the textfield and the keyboard input would actually go to the textfield.
But I haven't tested that thoroughly
Negative. Setting focus outside an accepted text input control is the event that hides the iOS keyboard again.
-
@patrickkidd yes, but textInput is an accepted text input control, just that the keyboard call event isn't forwarded outside the quickwidget.
Thats at least what I read out of the bug report
-
@J.Hilk said in iOS keyboard doesn't activate when TextField receives focus:
@patrickkidd yes, but textInput is an accepted text input control, just that the keyboard call event isn't forwarded outside the quickwidget.
Thats at least what I read out of the bug report
How exactly would you transfer focus back to the TextField? I tried setting the keyboard focus back to the TextInput item like the following pseudo-snippet and the keyboard was hidden again. Is that what you were thinking about?
def beginFocus(self, text, textInputQQuickItem): quickWidget = QApplication.focusWidget() lineEdit.setFocus(Qt.MouseFocusReason) quickWidget.setFocus(Qt.MouseFocusReason) textInputQQuickItem.setFocus(True, Qt.MouseFocusReason)
-
if you take my example and only change the content of
IosLineEdit.qml
to this
import QtQuick 2.12 import QtQuick.Controls 2.12 import QLineEdit 1.0 TextInput { id:root onEditingFinished: parent.forceActiveFocus() QLineEdit{ id:backend onTextChanged: root.text = text onEditingFinished: root.parent.forceActiveFocus() } MouseArea{ anchors.fill: parent visible: Qt.platform.os === "ios" && !root.activeFocus onClicked: { backend.init(root.text) shiftFocus.start() } Timer{ id:shiftFocus interval: 10 running: false repeat: false onTriggered: root.forceActiveFocus() } } }
then the keyboard is called, focus shifted and text input has again all functionality
have to change my repo I guess ;)
-
@J.Hilk said in iOS keyboard doesn't activate when TextField receives focus:
if you take my example and only change the content of
IosLineEdit.qml
then the keyboard is called, focus shifted and text input has again all functionality have to change my repo I guess ;)
Calling
QQuickItem::forceActiveFocus(Qt.MouseFocusReason)
still does not help for me in my app. The keyboard does not appear for me in your example using the iOS Simulator either.Below is my own nearly complete drop-in solution that retains focus on the QLineEdit and has extra (and crucial) support for cursor positions and selection areas. It should be all the code you need. Note there is no mouse area, which causes problems in some cases where the TextInput is a child.
class LineEditBackEnd(QLineEdit, Debug): def __init__(self, parent): super().__init__(parent) self.setMaximumSize(QSize(0, 0)) self.move(-10, -10) self.lower() self.hide() # prevent a seg fault from calling `text` as property from qml @pyqtSlot(result=str) def getText(self): return self.text() @pyqtSlot(str, int, int, int) def beginFocus(self, text, cursorPos, selectionStart, selectionEnd): if self.parent() is None: self.setParent(QApplication.activeWindow()) self.blockSignals(True) self.setText(text) self.setCursorPosition(cursorPos) super().setSelection(selectionStart, selectionEnd-selectionStart) self.setFocus(Qt.MouseFocusReason) self.blockSignals(False) self.show() item.forceActiveFocus(Qt.MouseFocusReason) @pyqtSlot(str, int, int) def do_setSelection(self, text, start, end): self.blockSignals(True) if text != self.text(): self.setText(text) super().setSelection(start, end-start) self.blockSignals(False) @pyqtSlot(int, int, int) def do_setCursorPosition(self, pos, start, end): self.blockSignals(True) super().setCursorPosition(pos) super().setSelection(start, end-start) self.blockSignals(False) @pyqtSlot(result=int) def getCursorPosition(self): return self.cursorPosition() @pyqtSlot(result=int) def selectionStart(self): return super().selectionStart() @pyqtSlot(result=int) def selectionEnd(self): return super().selectionEnd() qmlRegisterType(LineEditBackEnd, 'LineEditBackEnd', 1, 0, 'LineEditBackEnd')
import QtQuick 2.12 import QtQuick.Controls 2.5 import QtQuick.Layouts 1.12 import LineEditBackEnd 1.0 // maybe a better text input that actually raises the kb on iOS TextInput { id: root LineEditBackEnd { id: backend } property bool syncing: false property bool ios: Qt.platform.os == 'ios' onFocusChanged: { if(ios && !syncing) { syncing = true backend.beginFocus(text, cursorPosition, selectionStart, selectionEnd) syncing = false } } onSelectionStartChanged: { if(ios && !syncing) { syncing = true backend.do_setSelection(text, selectionStart, selectionEnd) syncing = false } } onSelectionEndChanged: { if(ios && !syncing) { syncing = true backend.do_setSelection(text, selectionStart, selectionEnd) syncing = false } } onCursorPositionChanged: { if(ios && !syncing) { syncing = true backend.do_setCursorPosition(cursorPosition, selectionStart, selectionEnd) syncing = false } } Connections { target: backend onTextChanged: { if(ios && !syncing) { syncing = true root.text = backend.getText() root.cursorPosition = backend.getCursorPosition() syncing = false } } onCursorPositionChanged: function(_old, _new) { if(ios && !syncing) { syncing = true cursorPosition = _new syncing = false } } onSelectionChanged: { if(ios && !syncing) { syncing = true select(backend.selectionStart(), backend.selectionEnd()) syncing = false } } onEditingFinished: root.parent.forceActiveFocus() } }
-
@patrickkidd said in iOS keyboard doesn't activate when TextField receives focus:
@J.Hilk Scratch that, your solution may have better support for cursors and selection areas after all. Could be an error on my end. I'll do some more checking.
Hmm, I don't know. I wasn't able to port your solution into my project. The TextInput does not receive text events, or sometimes crashes when setting the text property, and does not support selection behavior. Not sure why. I guess I'll stick with mine which uses the default QtQuick selection behavior, until the Qt bug is fixed.