I’m working on making an in-game script editor for my game, and I’ve been trying to add a scroll bar to my multiline TextInput so that the line numbers and text input can scroll together. It works pretty good, but for some reason the Scroll Rect causes the InputField’s caret and selection highlighting to get messed up when I add any more lines. Here’s a .gif of the problem: Imgur
I’ve had a simmilar issue and compiled a workaround until Unity fixes the InputField functionality.
All the combinations of layous/content fitters didn’t yield proper display of the last line.
The only downside is that you can’t fix the height of the input field without a ScrollRect component, also in the example.
Also, this example doesn’t cover the ScrollRect following the caret nor the selection in the InputField, if someone does that please post it here!
I was having a similar problem. The solution I found was simply create a game-object, obj1, to assign as a parent to the game-object with the input-field component, obj2. Then have the scroll-rect move the parent, obj1, which contains the child with the input-field component, obj2.
—>ScrollRect
-------->Obj1 <---- ScrollRect moves this one
-------------->Obj2 <— This one has input field component
This is my modified version of InputFieldMod, removing the highlight and take into account for canvas scale, etc:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System.Collections;
namespace SweatyChair.UI
{
[ExecuteInEditMode]
[RequireComponent(typeof(InputField))]
public class InputFieldScroller : UIBehaviour
{
[Tooltip("The default row count in InputField, this will be ignored if a ScrollRect is assigned")]
[Range(1, 50)] [SerializeField] private int _minRowCount = 1;
[Tooltip("Scroll rect parent")]
[SerializeField] private ScrollRect _scrollRect = null;
private InputField _inputField;
private RectTransform _rectTransform;
// Layout
private LayoutElement _layoutElement;
private HorizontalOrVerticalLayoutGroup _parentLayoutGroup;
private Vector2 _scrollRectSize;
private float _canvasScaleFactor = 1;
protected override void Awake()
{
_inputField = GetComponent<InputField>();
_inputField.onValueChanged.AddListener(new UnityAction<string>(ResizeInput));
_rectTransform = GetComponent<RectTransform>();
CanvasScaler canvasScaler = GetComponentInParent<CanvasScaler>();
if (canvasScaler)
_canvasScaleFactor = canvasScaler.scaleFactor;
_layoutElement = GetComponent<LayoutElement>();
_parentLayoutGroup = transform.parent.GetComponent<HorizontalOrVerticalLayoutGroup>();
}
// Resize input field recttransform
private void ResizeInput()
{
ResizeInput(_inputField.text);
}
private void ResizeInput(string text)
{
// Current text settings
TextGenerationSettings settings = _inputField.textComponent.GetGenerationSettings(_inputField.textComponent.rectTransform.rect.size);
settings.generateOutOfBounds = false;
settings.scaleFactor = _canvasScaleFactor; // HACK: scale factor of settings not following the global scale factor... make sure it do
// Get text padding (min max vertical offset for size calculation)
float vecticalOffset = _inputField.placeholder.rectTransform.offsetMin.y - _inputField.placeholder.rectTransform.offsetMax.y;
// Preferred text rect height
float preferredHeight = (new TextGenerator().GetPreferredHeight(text, settings) / _canvasScaleFactor) + vecticalOffset + 10;
float minHeight;
// Default text rect height (fit to scroll parent or expand to fit text)
if (_scrollRect)
minHeight = _scrollRect.GetComponent<RectTransform>().rect.size.y;
else
minHeight = ((new TextGenerator().GetPreferredHeight("", settings) * _minRowCount) / _canvasScaleFactor) + vecticalOffset;
// Current text rect height
float currentHeight = _inputField.textComponent.rectTransform.rect.height;
// Force resize
if (Mathf.Abs(currentHeight - preferredHeight) > Mathf.Epsilon) {
float newHeight = Mathf.Max(preferredHeight, minHeight); // At least min height
if (_parentLayoutGroup && _layoutElement)
_layoutElement.preferredHeight = newHeight;
else
_rectTransform.sizeDelta = new Vector2(_rectTransform.rect.width, newHeight);
}
// Scroll to bottom if just added new line
if (gameObject.activeInHierarchy && _inputField.caretPosition == _inputField.text.Length && _inputField.text.Length > 0 && _inputField.text[_inputField.text.Length - 1] == '
')
StartCoroutine(ScrollToBottomCoroutine());
}
// Update scroll rect position (after Layout was rebuilt)
private IEnumerator ScrollToBottomCoroutine()
{
yield return new WaitForEndOfFrame();
if (_scrollRect != null)
_scrollRect.verticalNormalizedPosition = 0;
}
}
}