InputField forced-casing with CharacterValidation.Name?

I love the UI.InputField object, especially its character validation, to prevent users from entering letters in a number field, or #s in a Name field.

But I’m seeing an issue where, on fields with Content Type = Name (or a custom type using InputField.CharacterValidation.Name), the field auto-capitalizes the first letter, and auto-lowercases the rest. No combination of Caps Lock or Shift seems to change it. Even if you copy text like “AAA” and paste it in, you see “Aaa” appear in the field.

Is there a way to not have that happen, but still get the input field to reject numbers or invalid symbols?

Note: it’s actually a bit more complicated it seems. The field allows spaces, after each space the first letter is again auto-capped. Also, even though some symbols are allowed, like apostrophe ('), the hyphen (-) is not. I went to school with one LaRod Stephens-Howling, so both his first and last name could not be entered correctly under these rules. Hyphenated last names are becoming very common these days, so maybe this should be considered a bug?

Well, as @MMOERPG said when using the onValidateInput delegate it actually replaces the built-in validation method. Since the whole UI library is open source you have multiple options:

  • Download the UI source code, add your desired changes and build your own version of the UnityEngine.UI.dll.
  • Actually use “onValidateInput” delegate and copy all the built-in validation code into your own callback and add the changes you want.
  • Or create a subcass of InputField and implement a wrapper for the “Validate” method. Unfortunately the Validate method is not virtual so we can’t just override it. However you can implement a new method which will call the old Validate. You just have to subscribe your new method to the “onValidateInput” event. Inside your method you would first call the original Validate. This will give you back a character which might have been modified. Now just add your own logic. You have the original character typed and the modified one. Usually you would return the modified one. Though you can still change anything you like.

The last option might be the easiest to implement. For example when you want to prevent a case change from uppercase to lower case you can simply do:

// inside your own validate method
char newChar = Validate(text, pos, ch);

if (ch != newChar && char.ToLower(ch) == newChar)
    return ch;

// add other things here

return newChar;

To prevent that certain characters are stripped you can do

if (newChar == '\0') // means the character should originally be stripped
{
    if (ch == '-')
        return ch;
}
return newChar;

You can either use the constructor to assign your method to the onValidateInput delegate or override the Awake / Start method. If you override Awake, make sure to call the base implementation as the “Selectable” base class hsa some logic in Awake.

Once you created your own subclass of the InputField you can simply use that one instead of the original InputField. Keep in mind that it is still a MonoBehaviour so the class name has to match the file name.

It would be way cleaner when Validate was implemented as virtual method. Strangely Append(char) is implemented virtual, but overriding Append would not work as it would mean you have to re-implement the whole method and add your additions there (in between the validation call and the insert). Though there’s another reason why it wouldn’t work ^^. They used the validation at another point, inside LateUpdate. So the best way is to use the onValidateInput delegate.

Another way would be to use reflection to get hold of the protected Validate method and create a delegate reference with CreateDelegate. Now you could implement an ordinary callback without the need of creating a subclass. While this would be even shorter, i wouldn’t recommend it ^^.

Hi @OmenX,

I do not believe it is possible to use CharacterValidation.Name but also customize it. I ended up just recreating the CharacterValidation.Name and adding my customization to it.

Here is what I came up with:

In any script, or a new one, find a reference to the input field component your want to validate.
Then, add this in the start function:

nameInputField.onValidateInput += ValidateInput;

Next add this function to the same script:

	public char ValidateInput(string text, int charIndex, char addedChar)			//Validates the text imput.
	{
		char temp = addedChar;
		char tempLastChar = '\0';
		if(text != ""){
			tempLastChar = text.ToCharArray()[text.Length - 1];
		}
		//Check if added care is a permitted character.
		addedChar = addedChar.ToString ().ToLower ().ToCharArray()[0];		//Set the Char input to lower to minimize checks.
		if (addedChar != 'q' && addedChar != 'w' && addedChar != 'e' && addedChar != 'r' && addedChar != 't' && addedChar != 'y'
		    && addedChar != 'u' && addedChar != 'i' && addedChar != 'o' && addedChar != 'p' && addedChar != 'a' && addedChar != 's'
		    && addedChar != 'd' && addedChar != 'f' && addedChar != 'g' && addedChar != 'h' && addedChar != 'j' && addedChar != 'k'
		    && addedChar != 'l' && addedChar != 'z' && addedChar != 'x' && addedChar != 'c' && addedChar != 'v' && addedChar != 'b'
		    && addedChar != 'n' && addedChar != 'm' && addedChar != '\'' && addedChar != '-') {
			temp = '\0';
		} else {
			if (charIndex == 0 || tempLastChar == '\'' || tempLastChar == '-') {		//Set the text to upper if its the first Char, or after
				addedChar = addedChar.ToString ().ToUpper ().ToCharArray () [0];		//a hyphen or apostrophe.
			}
			//Do not allow the name to begin with a hyphen or apostrophe
			if (charIndex == 0 && addedChar == '\'' || charIndex == 0 && addedChar == '-') {
				addedChar = '\0';
			}

			//Do not allow more than one hyphen or apostrophe per name.
			if (addedChar == '\'' || addedChar == '-') {
				if (text.Contains ("'") || text.Contains ("-")) {
					addedChar = '\0';
				}
			}
			temp = addedChar;		//Set temp to the entered char

			//If the text is selected, then a lower case character is input, the selected text is deleted and replaced with the lower case input.
			//The following code corrects the lower case to upper case once an additional character is input.
			if (charIndex == 1) {
				tempLastChar = tempLastChar.ToString ().ToUpper ().ToCharArray () [0];
				if (nameInputField.isFocused) {
					nameInputField.text = tempLastChar.ToString ();
				} else if (lineageInputField.isFocused) {
					lineageInputField.text = tempLastChar.ToString ();
				}
			}
			if (text.Length >= 2) {
				if (text.ToCharArray () [charIndex - 2] == '\'' || text.ToCharArray () [charIndex - 2] == '-') {
					tempLastChar = tempLastChar.ToString ().ToUpper ().ToCharArray () [0];
					text = text.Remove (text.Length - 1, 1);
					text = text.Insert (text.Length, tempLastChar.ToString ());
					if (nameInputField.isFocused) {
						nameInputField.text = text;
					} else if (lineageInputField.isFocused) {
						lineageInputField.text = text;
					}
				}
			}
		}
		return temp;			//Finally return the entered Char.
	}

This validation function permits only letters, hyphens, and apostrophes. Spaces are not allowed. It auto capitalizes the first character and the first character after a hyphen or apostrophe. It only allows one hyphen or apostrophe. Finally, there is a problem when the user highlights the input “turning it blue” then types a new character, erasing the old text and replacing it with a single character. When this happens, the new character does not auto capitalize because the string that is sent to the function along with the character returns the highlighted string and there for the check if its the first character returns null. To fix this, the only thing I could come up with without using an update or coroutine function was to check when the next character is entered if the entered char is the second char, then capitalize the first. Similar issue happens if highlighted and overwritten after a hyphen or apostrophe and that is also corrected once the next character is entered. I will probably add a coroutine to make it perfect instead of the above fix. If anyone knows of a better way, please post!

Good luck, let me know if you have any questions or would like help customizing this for your specifications.