UITextField starting cursor position is wrong
You need to always update your UI from the main thread:
DispatchQueue.main.async {
textField.selectedTextRange = textField.textRange(from: textField.endOfDocument, to: textField.endOfDocument)
}
iOS textfield cursor position wrong
This problem raised due to the different character size of the
secure dot character
and simple character. Dot character are wider in size that's why when you disable secure text entry, character contracts but cursor stays at the same position.
Although this issue should not be in the latest Xcode but I had a same problem in Xcode 6.3.
Explanation:
When you click button to toggle the secureTextEntry
, set the becomeFirstResponder
again for your textField, it will invoke the keyboard observer and reset the cursor to right position.
textField?.isSecureTextEntry = !textField.isSecureTextEntry
if textField?.isFirstResponder {
textField?.becomeFirstResponder()
}
Hope this will help you.
c# iOS Can NOT position cursor in UITextField in position 1
Ok, finally got it to work. @Jack Hua's solution won't work for me because I cannot set the textfield as the FirstResponder
because there are a number of UIMaskedTextField
's on the scrollview, the masked text fields are not the first sub-views on the scrollview, BUT it did give me some ideas! I was beginning to suspect that the initialization of the textfield when the text property was being set was somehow screwing up the cursor position, I believe that is what's happening, just can't prove it as the initialization of the view is happening behind the scenes. BUT I suspect that the setting of the view's mask via the EditMask
property made the initialization happen sooner and made it possible to set the cursor position. This also sets the mask from the outset, removing any doubt from the users mind as to what the format of the field is supposed to be. Here's my "final" code:
class UIMaskedTextField : UITextField
{
private string editmask = "";
public String EditMask
{
get => editmask;
set
{
if ((value != ""))
{
editmask = value;
this.Text = editmask.Replace("#", "_"); ;
}
}
}
public UIMaskedTextField()
{
this.Delegate = new PhoneMaskTextViewDelegate(this);
}
}
class PhoneMaskTextViewDelegate : UITextFieldDelegate
{
private UIMaskedTextField MyParent;
int index = 0;
public PhoneMaskTextViewDelegate(UIMaskedTextField parent)
{
MyParent = parent;
}
public override void DidChangeSelection(UITextField textField)
{
// place the cursor in the first fill podition
int y = textField.Text.IndexOf("_");
if (y > -1)
{
var newPosition = textField.GetPosition(textField.BeginningOfDocument, y);
textField.SelectedTextRange = textField.GetTextRange(newPosition, newPosition);
}
}
public override bool ShouldChangeCharacters(UITextField textField, NSRange range, string replacementString)
{
const string maskCharacters = "()-/ ";
string newText = "";
int val;
if (replacementString != "")
{
int fieldlength = 10; // MyParent.EditMask.Length;
string text = textField.Text;
if (text == "")
{
newText = MyParent.EditMask.Replace("#", "_");
}
else
{
newText = text;
}
string totalChar = newText.Replace(" ", "");
totalChar = totalChar.Replace("(", "");
totalChar = totalChar.Replace(")", "");
totalChar = totalChar.Replace("-", "");
totalChar = totalChar.Replace("_", "");
if (Utils.IsNumeric(replacementString))
{
if ((totalChar + replacementString).Length <= fieldlength)
{
if (replacementString != "")
{
index = newText.IndexOf("_");
if (index > -1)
{
StringBuilder sb = new StringBuilder(newText);
char character = char.Parse(replacementString);
sb[index] = character;
newText = sb.ToString();
textField.Text = newText;
// Set cursor to next position
NSRange therange = new NSRange(index, 0);
UITextPosition start = textField.GetPosition(textField.BeginningOfDocument, therange.Location + 1);
UITextPosition end = textField.GetPosition(start, therange.Length);
textField.SelectedTextRange = textField.GetTextRange(start, end);
}
newText = "";
}
}
}
} else
{ // Backspace/Delete pressed
UITextRange position = textField.SelectedTextRange;
string x = position.ToString();
int positionofcursor = Convert.ToInt32(x.Substring(x.IndexOf("(") + 1, x.IndexOf(",") - x.IndexOf("(") - 1));
string characterInPosition = "";
// make sure we're not deleting a mask character
do {
positionofcursor -= 1;
if (positionofcursor > -1)
{
characterInPosition = textField.Text.Substring(positionofcursor, 1);
int j = maskCharacters.IndexOf(characterInPosition);
}else
{
break;
}
} while (maskCharacters.IndexOf(characterInPosition) > -1);
if (positionofcursor > -1)
{
StringBuilder sb = new StringBuilder(textField.Text);
sb[positionofcursor] = char.Parse("_");
textField.Text = sb.ToString();
NSRange therange = new NSRange(positionofcursor, 0);
UITextPosition start = textField.GetPosition(textField.BeginningOfDocument, therange.Location);
UITextPosition end = textField.GetPosition(start, therange.Length);
textField.SelectedTextRange = textField.GetTextRange(start, end);
}
}
return Int32.TryParse(newText, out val);
}
}
Getting and Setting Cursor Position of UITextField and UITextView in Swift
The following content applies to both UITextField
and UITextView
.
Useful information
The very beginning of the text field text:
let startPosition: UITextPosition = textField.beginningOfDocument
The very end of the text field text:
let endPosition: UITextPosition = textField.endOfDocument
The currently selected range:
let selectedRange: UITextRange? = textField.selectedTextRange
Get cursor position
if let selectedRange = textField.selectedTextRange {
let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start)
print("\(cursorPosition)")
}
Set cursor position
In order to set the position, all of these methods are actually setting a range with the same start and end values.
To the beginning
let newPosition = textField.beginningOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
To the end
let newPosition = textField.endOfDocument
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
To one position to the left of the current cursor position
// only if there is a currently selected range
if let selectedRange = textField.selectedTextRange {
// and only if the new position is valid
if let newPosition = textField.position(from: selectedRange.start, offset: -1) {
// set the new position
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
}
To an arbitrary position
Start at the beginning and move 5 characters to the right.
let arbitraryValue: Int = 5
if let newPosition = textField.position(from: textField.beginningOfDocument, offset: arbitraryValue) {
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}
Related
Select all text
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.endOfDocument)
Select a range of text
// Range: 3 to 7
let startPosition = textField.position(from: textField.beginningOfDocument, offset: 3)
let endPosition = textField.position(from: textField.beginningOfDocument, offset: 7)
if startPosition != nil && endPosition != nil {
textField.selectedTextRange = textField.textRange(from: startPosition!, to: endPosition!)
}
Insert text at the current cursor position
textField.insertText("Hello")
Notes
Use
textField.becomeFirstResponder()
to give focus to the text field and make the keyboard appear.See this answer for how to get the text at some range.
See also
- How to Create a Range in Swift
iOS Added programmatically UITextField does not scroll text to cursor position while editing
My particular issue was resolved by increasing height size from 20 to 25. Most probably it happened because iOS was not able to adjust text because of font size and textfield height.
let itemNameTextField = UITextField(frame: CGRect(x: 20, y: 40, width: textFieldWidth, height: 25))
In iOS, Cursor position is not able to update from the Started event using the SelectedTextRange
Its necessary to queue the change so that it occurs after Started event returns:
CoreFoundation.DispatchQueue.MainQueue.DispatchAsync( () => {
this.SelectedTextRange = this.GetTextRange(positionToSet, positionToSet);
});
Based on Leo Dabus' answer in swift.
Tested on iOS simulator, iPhone 11, iOS 15.0.
Related Topics
How to Make Async/Await in Swift
How to Make Uiscrollview Zoom in Only One Direction When Using Auto Layout
Problems with Cropping a Uiimage in Swift
Swift: Nil Is Incompatible with Return Type String
Skaction Works in Didmovetoview But Doesn't Works in Function
Saving an Image to Photos Library Using Swift 2.0
How to Change Uinavigationbar Per View Controller
Swift How to Add Background Color Just Around Text in a Label
How to Insert a Override Function into a If Else Statement
Mapbox Navigation Pod Unable to Install with Objectivec Where I am Using Xcode 9.2
How to Set Response Data into Todayextenstion Widget
Mysterious "Cryptographic Verification Failure" Error on MACos Sierra, Xcode 8
Tableview with Searchcontroller - Deinit Not Called
Swift: Extracting/Downcasting Cftype Based Coretext Types in a Cfarray
Multiple File Upload with Array of Parameters Using Alamofire
Reversing a Range Results in Mismatching Types
Could Not Cast Value of Type 'Nsnull' (0X10Aa1B600) to 'Nsstring' (0X10B4Dab48)