菜鸟教程小白 发表于 2022-12-13 01:02:08

c# - UIControllerView 不会从委托(delegate)函数更新


                                            <p><p>我想通过委托(delegate)方法更新 <code>UIViewController</code> 中的 <code>UIView</code>。</p>

<p>有草图:
用户从 <code>UIViewController</code> 触发下载,下载由一个类管理,当 <code>UploadAsyncData</code> 完成时,下载方法将处理后的响应传递给另一个类。该类将接收到的数据写入本地数据库。</p>

<p>我想通知用户将 <code>UIViewController</code> 从进程进度中扔到哪里。</p>

<p>从现在开始我做了什么:</p>

<ul>
<li><p>在 <code>UIViewController</code> 我有一个 <code>UIView</code> 代表一个 <code>Overlay</code> 上面有一些消息。 <code>UIView</code> 也被设置为一个静态帮助类,以帮助从外部引用它。</p></li>
<li><p>当用户按下下载按钮时,<code>Overlay</code> 会显示并通知用户操作已被触发。</p></li>
<li><p>将操作传递给下载类时,<code>Overlay</code> 文本会更新为“您的下载正在进行中”</p></li>
<li><p>下载完成后,下载类中处理响应的方法将 <code>Overlay</code> 文本更新为“下载完成,正在处理接收到的数据...”</p></li >
<li><p>由于收到的数据是一个 C# 对象,我从该对象调用 SaveToDatabase 方法。</p></li>
<li><p>SaveToDatabase 方法使用进度指示器更新 <code>Overlay</code>。</p></li>
</ul>

<p>到目前为止,除了 <code>SaveToDatabase</code> 步骤之外,所有步骤都正确更新了 <code>Overlay</code> 消息。
调试时,我可以看到更新 <code>Overlay</code> 文本的方法在每一步都被调用,但 <code>UIView</code> 文本没有更新。</p>

<p>我不能使用 <code>InvokeOnMainThread</code> 因为 SaveToDatabase 不是 <code>UIViewController</code> 我不明白为什么所有步骤都正确更新 UIView 但最后一个没有.</p>

<p>这是所有这一步的一些提取代码。</p>

<p><strong>主要方法</strong></p>

<pre><code>public class Program:UIViewController
{
    {...}
    public override void ViewDidLoad()
    {
      base.ViewDidLoad();
      AddOverlay();
    }

    public void AddOverlay()
    {
      var bounds = UIScreen.MainScreen.Bounds;
      bounds.Size = new CGSize(bounds.Size.Width, bounds.Size.Height);
      loadingOverlay = new LoadingOverlay(bounds);
      loadingOverlay.Hidden = true;

      View.Add(loadingOverlay);
      System.Diagnostics.Debug.WriteLine(this.GetType().Name + loadingOverlay.GetHashCode());
      Helpers.LoadingOverlay = loadingOverlay;
    }

    public void DisplayOverlay(string text)
    {
      if(loadingOverlay != null){
            View.BringSubviewToFront(loadingOverlay);
            System.Diagnostics.Debug.WriteLine(this.GetType().Name + loadingOverlay.GetHashCode());
            loadingOverlay.Hidden = false;
            loadingOverlay.SetLoadingLabel( text );
      }   
    }

    void Tablesource_OnRowSelected(object sender, eConversion5.DownloadTableSource.RowSelectedEventArgs e)
    {
      bool isReacheable = myParameters.IsReachable();
      if (isReacheable) {
            DisplayOverlay(&#34;Your download is about to start...&#34;);
            Request request = new Request(url, myParameters);
            request.CanBeSaved = false;
            request.Action = &#34;display&#34;;
            httpCommunication.DoRequest(request);
      }
    }
}
</code></pre>

<p><strong>处理downloadAction的方法</strong></p>

<pre><code>public class HttpCommunication
{
    {...}
    public HttpCommunication(Parametrage parameters, HttpResponseAction responseAction)
    {
      client = new XSWebClient();
      client.UploadDataCompleted += OnUploadDataCompleted;
    }

    public void DoRequest(Request JRequest)
    {
      {...}
      SendRequest();
    }

    void SendRequest()
    {
      {...}
      client.UploadDataAsync(Request.Uri, &#34;POST&#34;, bytes);
      {...}
    }

    void OnUploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
    {
      Helpers.UpdateOverlayMessage(&#34;Processing recieved response...&#34;);
      {...}
      HandleResponse handler = new HandleResponse(myParameters, e.Result);
      {...}
    }
}
</code></pre>

<p><strong>处理服务器响应的类</strong></p>

<pre><code>public class HttpResponseAction
{
    {...}
    public void ExecuteAction(HandleResponse handled, Request request)
    {
      {...}
      Helpers.UpdateOverlayMessage(&#34;Processing response...&#34;);
      {...}
      HandleTrainings(queryId, action, JsonConvert.DeserializeObject&lt;List&lt;TrainingContainer&gt;&gt;(json, Settings));
      {...}
    }

    void HandleFormation(string queryId, string action, object nestedResult)
    {
      {...}
      if (action == &#34;display&#34;) {
            result.SaveToDatabase();
            {...}
      }
      {...}
    }

}
</code></pre>

<p>最后一步,即不更新 UIView(之前的所有步骤都在正确更新它)</p>

<pre><code>public class TrainingContainer
{
    {...}
    public void SaveToDatabase()
    {
      if(SignList != null){
            Helpers.UpdateOverlayMessage(&#34;Updating sign list in progress, may be take a while...&#34;);
            int updated = 0;
            int total = SignList.Count();
            if(total &gt; 0){
                foreach (Training training in SignList) {
                  updated++;
                  float progress = (float) updated / total;
                  Helpers.UpdateProgressValue(progress); //From debbuging, i can see that this method is called, and Overlay object is the same throw all class calls from the starting point.
                  {...}
                }
            }
      }
      {...}
    }
}
</code></pre>

<p><strong>助手类</strong></p>

<pre><code>public static class Helpers
{
    public static LoadingOverlay LoadingOverlay;
    {...}
    public static void UpdateOverlayMessage(string message)
    {
      if(LoadingOverlay != null){
            StackTrace stackTrace = new StackTrace();
            System.Diagnostics.Debug.WriteLine(typeof(Helpers).Name + &#34; (called from &#34; + stackTrace.GetFrame(1).GetMethod().Name+ &#34;)&#34; + LoadingOverlay.GetHashCode());

            LoadingOverlay.SetLoadingLabel(message);
      }
    }

    public static void UpdateProgressValue(float progessValue)
    {
      if (LoadingOverlay != null) {
            StackTrace stackTrace = new StackTrace();
            System.Diagnostics.Debug.WriteLine(typeof(Helpers).Name + &#34; (called from &#34; + stackTrace.GetFrame(1).GetMethod().Name + &#34;)&#34; + LoadingOverlay.GetHashCode());
            LoadingOverlay.UpdateProgress(progessValue);
      }
    }
}
</code></pre>

<p>编辑:从对 Helper 的调用中记录的一些跟踪记录。</p>

<pre><code>Helpers &lt;UpdateOverlayMessage&gt; :Transmitting request to the server...(called from Apply) -2037862263
Helpers &lt;UpdateOverlayMessage&gt; :Processing recieved response...(called from Apply) -2037862263
Helpers &lt;UpdateOverlayMessage&gt; :Processing response...(called from Apply) -2037862263
Helpers &lt;UpdateOverlayMessage&gt; :Updating sign list in progress, may be take a while...(called from Apply) -2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
Helpers &lt;UpdateProgressValue&gt; (called from Apply)-2037862263
</code></pre>

<p>更新 2:
忘记显示 Overlay 类</p>

<pre><code>public class LoadingOverlay : UIView {
      // control declarations
      UIActivityIndicatorView activitySpinner;
      UILabel loadingLabel;
      UIProgressView progressView;

      public LoadingOverlay (CGRect frame) : base (frame)
      {
            // configurable bits

            BackgroundColor = UIColor.Black;
            Alpha = 0.55f;
            AutoresizingMask = UIViewAutoresizing.All;

            nfloat labelHeight = 22;
            nfloat labelWidth = Frame.Width - 20;

            // derive the center x and y
            nfloat centerX = Frame.Width / 2;
            nfloat centerY = Frame.Height / 2;

            // create the activity spinner, center it horizontall and put it 5 points above center x
            activitySpinner = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.WhiteLarge);
            activitySpinner.Frame = new CGRect (
                centerX - (activitySpinner.Frame.Width / 2) ,
                centerY - activitySpinner.Frame.Height - 20 ,
                activitySpinner.Frame.Width,
                activitySpinner.Frame.Height);
            activitySpinner.AutoresizingMask = UIViewAutoresizing.All;
            AddSubview (activitySpinner);
            activitySpinner.StartAnimating ();

            // create and configure the &#34;Loading Data&#34; label
            loadingLabel = new UILabel(new CGRect (
                centerX - (labelWidth / 2),
                centerY + 20 ,
                labelWidth ,
                labelHeight
            ));
            loadingLabel.BackgroundColor = UIColor.Clear;
            loadingLabel.TextColor = UIColor.White;
            loadingLabel.Text = &#34;Wait a moment...&#34;;
            loadingLabel.TextAlignment = UITextAlignment.Center;
            loadingLabel.AutoresizingMask = UIViewAutoresizing.All;
            AddSubview (loadingLabel);

            progressView = new UIProgressView();
            progressView.Progress = 0.0f;
            var screenParts = Frame.Width / 3;
            progressView.Frame = new CGRect(new CGPoint(screenParts,loadingLabel.Frame.Y + loadingLabel.Frame.Height + 20), new CGSize(screenParts,40));
            progressView.Hidden = true;

            AddSubview(progressView);
      }

      /// &lt;summary&gt;
      /// Fades out the control and then removes it from the super view
      /// &lt;/summary&gt;
      public void Hide ()
      {
            progressView.Progress = 0.0f;
            InvokeOnMainThread(() =&gt; {
                UIView.Animate(
                  0.5, // duration
                  () =&gt; { Alpha = 0; },
                  () =&gt; { RemoveFromSuperview(); }
                );
            });
      }

      public void SetLoadingLabel(String text)
      {
            InvokeOnMainThread(() =&gt; {
                loadingLabel.Text = text;
            });
      }

      public void UpdateProgress(float progressValue){
            if(progressView.Hidden){
                progressView.Hidden = false;
            }
            if(progressValue &gt; 1){
                progressValue = 1;
            }
            InvokeOnMainThread(() =&gt; {
                progressView.SetProgress(progressValue, true);
            });
      }
    }
</code></pre></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>确保在更新 UI 时它在主线程上运行。 InvokeOnMainThread 方法是在 NSObject 上定义的,所以让 Helper 类继承自 NSObject。所以 Helper 类可以改进如下:</p>

<pre><code>   public sealed class Helpers:NSObject
   {
         public LoadingOverlay LoadingOverlay;

         private static readonly Helpers instance = new Helpers();
         private Helpers(){}
         public static Helpers Instance
         {
            get
            {
                return instance;
            }
         }

         public void UpdateOverlayMessage(string message)
         {
            InvokeOnMainThread ( () =&gt; {
                if(LoadingOverlay != null){
                  StackTrace stackTrace = new StackTrace();
                  System.Diagnostics.Debug.WriteLine(typeof(Helpers).Name + &#34; (called from &#34; + stackTrace.GetFrame(1).GetMethod().Name+ &#34;)&#34; + LoadingOverlay.GetHashCode());

                  LoadingOverlay.SetLoadingLabel(message);
                }
            });
         }
         //So does the method &#34;UpdateProgressValue&#34;.
   }
</code></pre>

<p>在主方法中:</p>

<pre><code>Helpers.Instance.LoadingOverlay = loadingOverlay;
</code></pre>

<p>更新界面时:</p>

<pre><code>Helpers.Instance.UpdateOverlayMessage(&#34;...&#34;);
</code></pre></p>
                                   
                                                <p style="font-size: 20px;">关于c# - UIControllerView 不会从委托(delegate)函数更新,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/43386614/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/43386614/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: c# - UIControllerView 不会从委托(delegate)函数更新