1

No matter what I do, I can NOT set the cursor to position 1 in this string (___) ___-____ position 1 is the position immediately following the opening (. I'm doing this in the EditingStarted method of the delegate, As a guide, I'm following the code here. currently my code is:

    public override void EditingStarted(UITextField textField)
    {
        if (MyParent.EditMask != "")
        {
            textField.Text = MyParent.EditMask.Replace("#", "_");
            // Set cursor position
            NSRange therange = new NSRange(index, 0);
            UITextPosition start = textField.GetPosition(textField.BeginningOfDocument, therange.Length - 1);
            UITextPosition end = textField.GetPosition(start, therange.Length);
            textField.SelectedTextRange = textField.GetTextRange(start, end);
        }
    }

The cursor ALWAYS appears immediately following the closing ), nothing I do changes that. I have no idea why. I've tried getting the position of the first underscore:

int position = textField.Text.IndexOf("_");
NSRange therange = new NSRange(position, 0);

But again, that results in the cursor being positioned immediately after the closing ). Anyone see what I'm doing wrong?

**** Update ****

Just so folks understand the context, The code above is part of a class called UIMaskedTextFieldthat I created to handle all of my mask/text input formating in the user interface. That class is:

class UIMaskedTextField : UITextField
{
    public string EditMask { get; set; }

    public UIMaskedTextField()
    {
        this.Delegate = new MaskTextViewDelegate(this);
    }

}
class MaskTextViewDelegate : UITextFieldDelegate
{
    private UIMaskedTextField MyParent;
    int index = 0;
    public MaskTextViewDelegate(UIMaskedTextField parent)
    {
        MyParent = parent;
    }

    public override void EditingStarted(UITextField textField)
    {
        if (MyParent.EditMask != "")
        {
            textField.Text = MyParent.EditMask.Replace("#", "_");
            // Set cursor position
            int position = textField.Text.IndexOf("_");

            NSRange therange = new NSRange(position, 0);
            UITextPosition start = textField.GetPosition(textField.BeginningOfDocument, therange.Location);
            UITextPosition end = textField.GetPosition(start, therange.Length);
            textField.SelectedTextRange = textField.GetTextRange(start, end);

        }
    }
    public override bool ShouldChangeCharacters(UITextField textField, NSRange range, string replacementString)
    {
        int fieldlength = 10; // MyParent.EditMask.Length;
        string text = textField.Text;
        int NeedASpace = MyParent.EditMask.IndexOf(" ");
        string newText = "";
        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("_", "");

        int val;
        if ((totalChar + replacementString).Length <= fieldlength) {
            if (replacementString != "")
            {
                index = newText.IndexOf("_");
                StringBuilder sb = new StringBuilder(newText);
                char character = char.Parse(replacementString);
                sb[index] = character;
                newText = sb.ToString();


                textField.Text = newText;

                // Set cursor to next position, this works flawlessly
                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
            { // Still working the backspace key, so not done here yet.
                if (text.Length > 1)
                {
                    newText = text.Substring(0, text.Length - 1);
                }
                else
                {
                    newText = "";
                }
            }
        }

        return Int32.TryParse(newText, out val);
    }
}
Prescott Chartier
  • 1,519
  • 3
  • 17
  • 34

2 Answers2

1

To position cursor in UITextField in position 1:

public partial class ViewController : UIViewController
{
    public ViewController (IntPtr handle) : base (handle)
    {
    }

    UITextField textfield1 { get; set; }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        // Perform any additional setup after loading the view, typically from a nib.

        var frame = new CGRect(10, 10, 300, 40);
        textfield1 = new UITextField(frame);

        textfield1.Text = "(___) ___-____";
        textfield1.Delegate = new myDelegate();

        View.Add(textfield1);


        textfield1.BecomeFirstResponder();

    }
}

public class myDelegate : UITextFieldDelegate {

    public override void EditingStarted(UITextField textField)
    {
        var arbitraryValue = 1;

        var newPosition = textField.GetPosition(textField.BeginningOfDocument, arbitraryValue);
        textField.SelectedTextRange = textField.GetTextRange(newPosition, newPosition);
    }
}

Update:

public override void DidChangeSelection(UITextField textField)
{
    var arbitraryValue = 1;

    var newPosition = textField.GetPosition(textField.BeginningOfDocument, arbitraryValue);
    textField.SelectedTextRange = textField.GetTextRange(newPosition, newPosition);

}
nevermore
  • 15,432
  • 1
  • 12
  • 30
  • Can't do this in `ViewDidLoad`, has to be done in the delegate.so when the user selects the field to enter data, they are presented with the mask and the cursor is placed at the first "replaceable" character, in this case, the underscore. The `textField` is part of a larger `ScrollView` which contains many `TextFields`. In the `ShouldChangeCharacters` method of the delegate, I am able to place the cursor to the right of the last character entered with code I had later copied to to `EditingStarted, but it doesn't work there. – Prescott Chartier Aug 06 '20 at 13:05
  • Should have done in which delegate? No matter it is in ViewDidLoad or part of a larger ScrollView, the code in the EditingStarted will work. If there are many textfields, you can give each one a tag and check the tag in the EditingStarted to know if it is the correct textfield you want to change. – nevermore Aug 07 '20 at 01:36
  • Hua My apologies, you don't understand the context of what I'm doing. I don't need a tag to know which `TextField` I'm in. Please look at my updated code. – Prescott Chartier Aug 07 '20 at 02:31
  • Sorry, I'm not very clear about your question even if I checked your updated code. If you want to change the cursor position in the UITextfield, you can use my code and you can position cursor in any position(just change the arbitraryValue). Also, look at [this thread](https://stackoverflow.com/questions/34922331/getting-and-setting-cursor-position-of-uitextfield-and-uitextview-in-swift) may help. – nevermore Aug 07 '20 at 07:18
  • Hua I've seen that thread, doesn't help.as it is exactly the same as I'm using which doesn't work. – Prescott Chartier Aug 07 '20 at 13:25
  • Can you please share me a Minimal, Reproducible Example? – nevermore Aug 10 '20 at 08:49
  • Hua The code in my question produces the result every time. Just create a view and place a `UIMaskedTextField` on it and you will see the result of the cursor placement. The only time the placement is correct is if I specify a range of zero to one, which results in the first character being selected. That's as close as I can get to placing the cursor in position 1. – Prescott Chartier Aug 10 '20 at 15:09
  • Hua Just so you know, in the `ShouldChangeCharacters` method of my delegate code I can place the cursor very precisely with no issues. I've reported this issue as a bug here https://developercommunity2.visualstudio.com/myfeedback?port=1025&fsid=f7201069-eb32-4287-baf6-eb3a2fdd8351&entry=myfeedback. – Prescott Chartier Aug 10 '20 at 15:11
  • Try to call the method in DidChangeSelection. See my update answer. – nevermore Aug 11 '20 at 07:51
  • That didn't work either, exact same issue, the cursor is placed immediately after the `)`. – Prescott Chartier Aug 11 '20 at 13:23
  • Also, for some reason the `DidChangeSelection` was called 3 times for the one change, Not sure why that was. – Prescott Chartier Aug 11 '20 at 13:46
  • I upload my sample project [here](https://github.com/XfHua/TextField-cursor). Please check and correct me if I am wrong, – nevermore Aug 12 '20 at 01:30
0

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);
    }
}
Prescott Chartier
  • 1,519
  • 3
  • 17
  • 34