- Home /
Masked InputField on Unity UI
I am making a player registration menu in a game using data such as: Name, Email, Password, Gender, Date of Birth and Phone Number.
The problem is when I add the UI InputField for the Date of Birth and Phone Number, I would need to apply a formatting mask such as "##/ ##/####" for the Date and "(##) #####-####" for the Phone.
I can limit the InputField to typing just numbers, however I did not find any way to add formatted masks or to create a component akin to C#'s MaskedTextBox. Is it possible to do something similar to this in Unity?
Example of MaskedTextBox:
The mask's behaviour I need is exactly like this: https://raw.githubusercontent.com/viniciusmo/V$$anonymous$$askTextField/master/Resources/vmasktextfield.gif
Answer by Nokaut · Feb 03, 2017 at 08:37 PM
Hi @IntrudeN313, i had a similar problem with Dates. Maybe this code can help you
private char OnValidateInput(string text, int charIndex, char addedChar)
{
if (addedChar == '/')
return addedChar;
if (!char.IsNumber(addedChar))
return '\0';
//I Want date format like (dd/mm/yyyy)
switch(text.Length)
{
case 2: //User typed 'dd'
DataInicial.text = text + "/";
DataInicial.caretPosition = DataInicial.text.Length;
break;
case 5: //User typed 'dd/mm'
DataInicial.text = text + "/";
DataInicial.caretPosition = DataInicial.text.Length;
break;
}
return addedChar;
}
Answer by xxShana · Apr 24, 2020 at 11:34 AM
public class PhoneInputMask : MonoBehaviour
{
public InputField inputField;
private void OnValidate()
{
inputField = GetComponent<InputField>();
}
public void Start()
{
inputField.onValueChanged.AddListener(delegate { OnValueChangeEvent(); });
}
// Invoked when the value of the text field changes.
public void OnValueChangeEvent()
{
if (string.IsNullOrEmpty(inputField.text))
{
inputField.text = string.Empty;
}
else
{
string input = inputField.text;
string MatchPattern = @"^((\d{2}\.){0,4}(\d{1,2})?)$";
string ReplacementPattern = "$1.$3";
string ToReplacePattern = @"((\.?\d{2})+)(\d)";
input = Regex.Replace(input, ToReplacePattern, ReplacementPattern);
Match result = Regex.Match(input, MatchPattern);
if (result.Success)
{
inputField.text = input;
inputField.caretPosition++;
}
}
}
}
Answer by Hellium · Jan 05, 2017 at 05:38 PM
UPDATED ANSWER
public void OnValueChanged(string input) // Supposing input = "953"
{
int index = 0 ;
string format = "##/##/####" ;
string output = format;
for( int i = 0 ; i < input.Length ; ++i )
{
index = output.IndexOf("#");
if( index < 0 )
break ;
if( index == 0 )
output = input[i] + output.Substring ( 1 );
else if( index == output.Length - 1 )
output = output.Substring ( 0, index ) + input[i] ;
else
output = output.Substring ( 0, index ) + input[i] + output.Substring ( index + 1 );
}
if( index >= 0 )
output = output.Substring( 0, index + 1 );
Debug.Log( output ); // Will output 95/3
}
2nd UPDATE
public UnityEngine.UI.InputField inputField;
// The Text component showing your formatted string
public UnityEngine.UI.Text text;
private string lastValidInput = string.Empty;
private void Awake()
{
inputField.onValueChanged.AddListener( OnValueChanged );
}
private void OnValueChanged( string input ) // Supposing input = "9534"
{
string format = "{0}{1}/{2}{3}/{4}{5}{6}{7}" ;
string[] array = new string[input.Length];
int index = format.IndexOf("{" + (input.Length - 1) + "}");
if ( input.Length == 0 )
{
text.text = string.Empty;
return;
}
if ( index >= 0 )
{
format = format.Substring( 0, index + 3 );
}
else
{
inputField.text = lastValidInput;
return;
}
for ( index = 0 ; index < input.Length ; ++index )
array[index] = string.Empty + input[index];
lastValidInput = input;
text.text = string.Format( format, array ); // Will output 95/34
}
2nd Update' (different output)
private void OnValueChanged( string input ) // Supposing input = "9534"
{
string format = "{0}{1}/{2}{3}/{4}{5}{6}{7}" ;
string[] array = new string[input.Length];
int index = format.IndexOf("{" + (input.Length) + "}");
if ( input.Length == 0 )
{
text.text = string.Empty;
return;
}
if ( index >= 0 )
{
format = format.Substring( 0, index );
}
else
{
inputField.text = lastValidInput;
return;
}
for ( index = 0 ; index < input.Length ; ++index )
array[index] = string.Empty + input[index];
lastValidInput = input;
text.text = string.Format( format, array ); // Will output 95/34/
}
IMPORTANT NOTE :
The functions I gave you will only accept a string containing numbers only. Thus, you will need an input field with invisible characters to accept the input and a Text component to show the formatted result (See 2nd Update)
INITIAL ANSWER
I think regular expressions is the way to go. I prepared you an example for your first case :
System.Text.RegularExpressions.Match match = System.Text.RegularExpressions.Regex.Match("9545551212", @"(\d{3})(\d{3})(\d{4})");
if ( match.Success )
{
// match.Groups[0] will return the whole input
string output = string.Format("({0}) {1}-{2}", match.Groups[1], match.Groups[2], match.Groups[3]);
Debug.Log( output ); // Will log "(954) 555-1212"
}
Unfortunately that doesn't work because to build the desired effect, I need to use it on InputField's Dynamic String callback OnValueChanged like that:
using UnityEngine;
using UnityEngine.UI;
using System.Text.RegularExpressions;
public class $$anonymous$$askedInputField : $$anonymous$$onoBehaviour {
private InputField _inputField;
private void Awake()
{
_inputField = GetComponent<InputField>();
}
public void OnValueChanged(string value)
{
var match = Regex.$$anonymous$$atch(value, @"(\d{3})(\d{3})(\d{4})");
if (match.Success) {
string output = string.Format("({0}) {1}-{2}", match.Groups[1], match.Groups[2], match.Groups[3]);
_inputField.text = output;
}
}
}
This way I did, the mask would be put only when I've inputted the last number, when it matches the Regex... I'm searching for something more dynamic on UI, maybe I'm using the wrong strategy. Anyway thanks for the help!
@IntrudeN313 : I gave you two other solutions. Tell me if it fits your needs this time ;)
I tested both solutions and the two works well, the only problem I found was on the caret and the selection that gets on InputField's invisible text and not on the OutputText:
Now code I'm using based on the first solution:
using UnityEngine;
using UnityEngine.UI;
public class $$anonymous$$askedInputField : $$anonymous$$onoBehaviour
{
[SerializeField] private Text _outputField;
private InputField _inputField;
private void Awake()
{
_inputField = GetComponent<InputField>();
}
public void OnValueChanged(string input)
{
int index = 0;
string format = "##/##/####";
string output = format;
for (int i = 0; i < input.Length; ++i) {
index = output.IndexOf("#");
if (index < 0) break;
if (index == 0)
output = input[i] + output.Substring(1);
else if (index == output.Length - 1)
output = output.Substring(0, index) + input[i];
else
output = output.Substring(0, index) + input[i] + output.Substring(index + 1);
}
if (index >= 0)
output = output.Substring(0, index + 1);
_outputField.text = output;
}
}
And the second solution:
using UnityEngine;
using UnityEngine.UI;
public class $$anonymous$$askedInputField : $$anonymous$$onoBehaviour
{
[SerializeField] private Text _outputField;
private InputField _inputField;
private string _lastValidInput = string.Empty;
private void Awake()
{
_inputField = GetComponent<InputField>();
}
public void OnValueChanged(string input)
{
string format = "{0}{1}/{2}{3}/{4}{5}{6}{7}";
string[] array = new string[input.Length];
int index = format.IndexOf("{" + (input.Length - 1) + "}");
if (input.Length == 0) {
_outputField.text = string.Empty;
return;
}
if (index >= 0) {
format = format.Substring(0, index + 3);
} else {
_inputField.text = _lastValidInput;
return;
}
for (index = 0 ; index < input.Length; ++index)
array[index] = string.Empty + input[index];
_lastValidInput = input;
_outputField.text = string.Format(format, array);
}
}
Thanks for the answer @Hellium I try using the "2nd update" option but does not work for formats that have more than 9 digits, so I made a little change to your code and now it works as expected:
public UnityEngine.UI.InputField inputField;
// The Text component showing your formatted string
public UnityEngine.UI.Text text;
private string lastValidInput = string.Empty;
private void Awake()
{
inputField.onValueChanged.AddListener( OnValueChanged );
}
private void OnValueChanged( string input ) // Supposing input = "12345678901"
{
string format = "{0}{1}/{2}{3}/{4}{5}{6}{7}/{8}{9}{10}" ;
string[] array = new string[input.Length];
int index = format.IndexOf("{" + (input.Length - 1) + "}");
if ( input.Length == 0 )
{
text.text = string.Empty;
return;
}
if ( index >= 0 )
{
int n = inputField.text.Length;
int toAdd = (n == 1) ? 3 : $$anonymous$$athf.CeilToInt($$anonymous$$athf.Log10(n)) + 2;
format = format.Substring( 0, index + toAdd );
}
else
{
inputField.text = lastValidInput;
return;
}
for ( index = 0 ; index < input.Length ; ++index )
array[index] = string.Empty + input[index];
lastValidInput = input;
text.text = string.Format( format, array ); // Will output 12/34/5678/901
}
Answer by RobAnthem · Jan 05, 2017 at 05:59 PM
Take a look at .NET's formatting System.String,Format It should cover basically everything you are looking to do.
I looked at it, but the problem isn't the format, it's how I can make the Unity UI InputField work like the ones in this GIF: https://raw.githubusercontent.com/viniciusmo/V$$anonymous$$askTextField/master/Resources/vmasktextfield.gif
Thank you for the help!