Displaying progress

In this recipe, we will discuss how to display the progress of known length.

Getting ready

In this recipe, we will talk about the UIProgressView control. This control provides a similar functionality to the ProgressBar control in .NET. Create a new iPhone Single View Application project in Xamarin Studio and name it ProgressApp.

How to do it...

The following are the steps for using the UIProgressView class. Note that in this recipe, we will add all the controls programmatically without the use of Interface Builder.

  1. Add the following using directives in the ProgressAppViewController class file:
    using System.Drawing;
    using System.Threading;
    using System.Threading.Tasks;
  2. Add the following fields in the class:
    UILabel labelStatus;
    UIButton buttonStartProgress;
    UIProgressView progressView;
    float incrementBy = 0f;
  3. Enter the following code in the ViewDidLoad override:
    // Initialize the label
    this.labelStatus = new UILabel (new RectangleF (60f, 60f, 200f, 50f));
    this.labelStatus.AdjustsFontSizeToFitWidth = true;
    // Initialize the button
    this.buttonStartProgress = UIButton.FromType (UIButtonType.System);
    this.buttonStartProgress.Frame = new RectangleF (60f, 400f, 200f, 40f);
    
    this.buttonStartProgress.SetTitle ("Tap to start progress!", UIControlState.Normal);
    this.buttonStartProgress.TouchUpInside += delegate {
      // Disable the button
      this.buttonStartProgress.Enabled = false;
      this.progressView.Progress = 0f;
      // Start a progress
      Task.Factory.StartNew(this.StartProgress);
    } ;
    
    // Initialize the progress view
    this.progressView = new UIProgressView (new RectangleF (60f, 200f, 200f, 50f));
    
    // Set the progress view's initial value
    this.progressView.Progress = 0f;
    
    // Set the progress increment value
    // for 10 items
    this.incrementBy = 1f / 10f;
    
    this.View.AddSubview(this.labelStatus);
    this.View.AddSubview(this.buttonStartProgress);
    this.View.AddSubview(this.progressView);
  4. Add the following method in the class:
    private void StartProgress ()
    {
      float currentProgress = 0f;
      while (currentProgress < 1f)
      {
        Thread.Sleep(1000);
        this.InvokeOnMainThread(delegate {
          // Advance the progress
          this.progressView.Progress += this.incrementBy;
          currentProgress = this.progressView.Progress;
          // Set the label text
          this.labelStatus.Text = string.Format("Current value: { 0}", Math.Round((double)this.progressView.Progress, 2));
          if (currentProgress >= 1f)
          {
            this.labelStatus.Text = "Progress completed!";
            this.buttonStartProgress.Enabled = true;
          }//end if
        } );
      }//end while
    }
  5. Compile and run the app on the simulator. Tap on the button and watch the progress bar fill.

How it works...

The current value of UIProgressView is represented by its Progress property. Its acceptable value range is always from 0 to 1. So, when we initialize it, we set it to 0 to make sure that the bar is not filled at all. This can be done using the following code:

this.progressView.Progress = 0f;

Since UIProgressView has a specific range, we need to assign the value we want it to be incremented by, depending on the number of items we need to process (in this case, 10) using the following code:

this.incrementBy = 1f / 10f;

In the button's TouchUpInside handler, we disable the button and start our progress through Task from System.Threading.Tasks, as shown in the following code:

this.buttonStartProgress.TouchUpInside += delegate {
  // Disable the button
  this.buttonStartProgress.Enabled = false;
  this.progressView.Progress = 0;
  // Start a progress
  Task.Factory.StartNew(this.StartProgress);
};

In the StartProgress() method, we start a loop that will process the work, which needs to be done. Since the work executes on a separate thread, when we want to make changes to the controls, it must be done on the main UI thread by calling the InvokeOnMainThread method, which accepts a parameter of the NSAction type. An NSAction type parameter can accept anonymous methods as well, as seen in the following code:

this.InvokeOnMainThread(delegate {
  // Advance the progress
  this.progressView.Progress += this.incrementBy;
  currentProgress = this.progressView.Progress;
  // Set the label text
  this.labelStatus.Text = string.Format("Current value: { 0}", Math.Round((double)this.progressView.Progress, 2));
  if (currentProgress >= 1f)
  {
    this.labelStatus.Text = "Progress completed!";
    this.buttonStartProgress.Enabled = true;
  }//end if
});

There's more...

The progress view supports two styles. UIProgressViewStyle.Default (the one that was used in this recipe) and UIProgressViewStyle.Bar. There is absolutely no functionality difference between the two styles, except for appearance. To change the style of the progress view, set its Style property to one of the previously mentioned values.

UIProgressView height

Setting the height of the progress view has no effect, as it is constant for the control. For creating a variable-height progress bar, the UIProgressView class must be subclassed.

See also

  • The Receiving user input with buttons recipe