Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Forcing Scrollview Content to refresh after button press

Discussion in 'Getting Started' started by Redlar, Mar 28, 2018.

  1. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    Just as the title says, is there a way to force a Scrollview to update its child text without using Update() or FixedUpdate()?

    What I've done is link the text to a button, and when I click that button the object.text updates. However, it doesn't update in Canvas until I press the button again, and I would like to force it to update after calling the ReadTxt function. I would like to just add a line in ReadTxt that will refresh the field just once, if that's possible.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.IO;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class TextTest : MonoBehaviour {
    8.     public Button gen1;
    9.     public Text ordoBG;
    10.     public InputField ordoList;
    11.     public ScrollRect ordoRect;
    12.  
    13.     public TextAsset[] bgs;
    14.  
    15.     private string lines;
    16.  
    17.     // Use this for initialization
    18.     void Start () {
    19.         //Canvas.ForceUpdateCanvases ();
    20.         gen1.onClick.AddListener (WriteTxt);
    21.     }
    22.    
    23.     void WriteTxt () {
    24.         if (ordoList.text == "No Ordo") {
    25.             ReadTxt (0);
    26.         } else if (ordoList.text == "Ordo Hereticus") {
    27.             ReadTxt (1);
    28.         } else if (ordoList.text == "Ordo Malleus") {
    29.             ReadTxt (2);
    30.         } else if (ordoList.text == "Ordo Xenos") {
    31.             ReadTxt (3);
    32.         } else if (ordoList.text == "Ordo Sicarius") {
    33.             ReadTxt (4);
    34.         } else if (ordoList.text == "Ordo Sepulturum") {
    35.             ReadTxt (5);
    36.         } else if (ordoList.text == "Ordo Scriptorum") {
    37.             ReadTxt (6);
    38.         } else if (ordoList.text == "Ordo Machinum") {
    39.             ReadTxt (7);
    40.         } else if (ordoList.text == "Ordo Custodum") {
    41.             ReadTxt (8);
    42.         } else if (ordoList.text == "Ordo Chronos") {
    43.             ReadTxt (9);
    44.         } else if (ordoList.text == "Ordo Obsoletus") {
    45.             ReadTxt (10);
    46.         }else
    47.             return;
    48.     }
    49.  
    50.     void ReadTxt(int array){
    51.         string testTxt = ordoBG.text;
    52.         lines = bgs [array].text;
    53.         print (lines);
    54.         testTxt = lines;
    55.         ordoBG.text = testTxt.ToString ();
    56.         ordoBG.GraphicUpdateComplete ();
    57.     }
     
  2. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I don't understand what you mean. There is no "refresh" for Canvas elements. They automatically draw their current content on every frame. The only way you could stop that would be to block the main thread entirely.
     
  3. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    I'm not sure what I might have done wrong, but when I press the button in question other elements that are linked to that button, such as some dropdown and text fields, update instantly. The scrollview, on the other hand, looks at one of the text fields and the text property is updated to be one of the textassets I have linked. However, the scrollview itself remains blank, and the text does not appear on-screen until I click the button again.

    Basically, the text generated by the previous button click is what shows up when I press the button again, even if the value being compared against has changed. The scrollview never changes until that button is pressed, unless I check the buttonpress in Update() but then it runs hundreds of time per frame.

    Now that I write all that out, I wonder if I can just solve this by having a counter to tell Unity to run the function only once during Update().
     
  4. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    20,951
    After some extension research (which included watching an optimization video which was very informative for my uses but totally not helpful for this situation), my suggestion would be to try one of these after you've modified the contents. Just be aware that the second method is very performance heavy.

    Code (csharp):
    1. LayoutRebuilder.ForceRebuildLayoutImmediate(ordoRect);
    Code (csharp):
    1. Canvas.ForceUpdateCanvases();
     
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I've used the layout rebuilder, sometimes, but it's still odd that it isn't updated right away for the OP; this doesn't seem like a situation I would think anything special is required.
     
    JoeStrout likes this.
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I'd like to see the code for a simple example. I think there must be something sketchy going on.
     
  7. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    Thank you for all the help. I still couldn't get it working with LayoutRebuilder or Canvas, though I think I found why.

    I've debugged the code a little bit more, and it seems that the way I've structured the code, since the text field which is checked by WriteTxt is empty at the start of the program, it doesn't look for the appropriate TextAsset until the next time the button is clicked, at which point it checks against the old, now-filled text field and displays the appropriate text for that old value, which has since changed.

    So it seems that what I need to do is find a way for this code to either execute immediately after the text field is updated, or transfer this code to the main script controlling the button and generating the values. I think.
     
    Last edited: Mar 29, 2018
  8. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I don't believe you've gotten to the heart of the matter yet. The problem is somewhere in your code.

    I suggest you try to create a minimal example, either by stripping down (a copy of) your scene, or by making a new scene from scratch. In so doing, you will most likely discover the source of the problem. And if not, at least you'll have a minimal example to submit for help.
     
  9. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    Stripped down, my code looked like this:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.IO;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class DupTest : MonoBehaviour {
    8.     public Button gen1;
    9.     public Text text;
    10.     public InputField input;
    11.     public ScrollRect scroller;
    12.  
    13.     public TextAsset[] bgs;
    14.  
    15.     private string lines;
    16.     private int clicked;
    17.  
    18.     // Use this for initialization
    19.     void Awake () {
    20.         //Canvas.ForceUpdateCanvases ();
    21.         gen1.onClick.AddListener (WriteTxt);
    22.     }
    23.  
    24.     void WriteTxt () {
    25.         if (input.text == "Word Value") {
    26.             ReadTxt (0);
    27.         } else if (input.text == "Second Value") {
    28.             ReadTxt (1);
    29.         } else
    30.             clicked++;
    31.         print ("Gave up" + clicked);
    32.     }
    33.  
    34.     void ReadTxt(int array){
    35.         print ("I'm Awake!");
    36.         string testTxt = text.text;
    37.         lines = bgs [array].text;
    38.         print (lines);
    39.         testTxt = lines;
    40.         text.text = testTxt.ToString ();
    41.     }
    42. }
    43.  
    And right now it looks like this:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using System.IO;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6.  
    7. public class DupTest : MonoBehaviour {
    8.     public Button gen1;
    9.     public Text text;
    10.     public InputField input;
    11.     public ScrollRect scroller;
    12.  
    13.     public TextAsset[] bgs;
    14.  
    15.     private string lines;
    16.     private int clicked;
    17.  
    18.     // Use this for initialization
    19.     void Awake () {
    20.         //Canvas.ForceUpdateCanvases ();
    21.         gen1.onClick.AddListener (WriteTxt);
    22.     }
    23.  
    24.     void WriteTxt () {
    25.         StartCoroutine (delaying(1));
    26.     }
    27.  
    28.     void ReadTxt(int array){
    29.         print ("I'm Awake!");
    30.         string testTxt = text.text;
    31.         lines = bgs [array].text;
    32.         print (lines);
    33.         testTxt = lines;
    34.         text.text = testTxt.ToString ();
    35.     }
    36.  
    37.     IEnumerator delaying(float delay){
    38.         delay = 0.1f;
    39.         yield return new WaitForSeconds (delay);
    40.         print ("Delayed");
    41.         if (input.text == "Word Value") {
    42.             ReadTxt (0);
    43.         } else if (input.text == "Second Value") {
    44.             ReadTxt (1);
    45.         } else
    46.             clicked++;
    47.         print ("Gave up" + clicked);
    48.         //return;
    49.     }
    50. }
    Calling a co-routine with a delay makes it work. With the delay, it doesn't check the Text value of the InputField until after the main generation script, which changes the value of that InputField when Button gen1 is clicked, has set what the value in the InputField is. Then it calls the correct TextAsset and displays it in the ScrollRect.
     
  10. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    The way you're using buttons is funny. Instead of getting a Button reference, and using that to call AddListener, why not hook up the event handlers directly (in the Inspector)? (And note that if you do hook up events in code like this, you need to remember to call RemoveListener in OnDisable or OnDestroy.)

    I'm still not entirely clear on what you're saying about the trouble, but I suspect it's just a matter of, your events were being invoked in a different order than what you expected. And the way you're hooking them up, you wouldn't have much control over that. But if you hook them up in the Inspector in the standard way, then you can easily see and manipulate the order (the event handlers get invoked in the order they're listed in the Inspector). So this problem would go away.
     
  11. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    What do you mean by hook up the event handlers directly in the inspector?
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    In the inspector for a button, there is a section "OnClick (event)". You can click the '+' sign there, and it will add an event. You can click as many as you want.

    Then ,you drag n drop the game object that has the script you want to use into the proper slot. Next, you use the drop down menu to find the script, then the method you wish to call. :)
     
  13. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    Huh. Interesting. I didn't realize that was there.

    I tried putting my gameobject, I didn't see the methods that I was using in the code to change the text.
     
    Last edited: Mar 30, 2018
  14. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    I can't see your screenshot, but methods only show up there if they are public. :)
     
    JoeStrout likes this.
  15. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
  16. Redlar

    Redlar

    Joined:
    Aug 17, 2016
    Posts:
    16
    That would explain it.

    Thank you, that's actually really helpful.
     
    JoeStrout likes this.