This is Part 3 of our series building a xib-free iOS app with C#.

In our last episode, we created a new project in Xamarin Studio, created a custom view controller, initialized its view, and added a clock view. Today, we will explore several ways to display and update the time.

Display the Current Time

To display the current time, add a method to the class CurrentWeatherClockViewController to update the clock view:

[code lang=text] private void UpdateClockView() { timeLabel.Text = String.Format (“{0:hh:mm:ss tt}”, DateTime.Now); } [/code]

Next, override the ViewWillAppear method of the super class, UIViewController:

[code lang=text] public override void ViewWillAppear (bool animated) { base.ViewWillAppear (animated);

this.UpdateClockView (); } [/code]

A shortcut for entering this method is to first type override. As soon as you type a space after override, Xamarin Studio will provide a completion list. Choose ViewWillAppear from that list. The IDE will then automatically insert a skeleton of the ViewWillAppear method, including the call to base.ViewWillAppear. All you have to do is add this line:

[code lang=text] this.UpdateClockView (); [/code]

When you build and run the project, your app will display the current time. But the time is still not being updated like a real clock. Let’s do that now.

Update the Current Time

To update the time, we need a timer. Add a private field:

[code lang=text] private NSTimer timer; [/code]

Note that the IDE sets NSTimer in red. Right click and choose Resolve > using MonoTouch.Foundation.

Add a method to start the timer:

[code lang=text] private void StartTimer() { this.timer = NSTimer.CreateRepeatingScheduledTimer (TimeSpan.FromSeconds (1.0), delegate { this.UpdateClockView (); }); } [/code]

And start it by adding this line to ViewWillAppear:

[code lang=text] this.StartTimer (); [/code]

Your CurrentWeatherClockViewController class should look something like this now:

[code lang=text] using System; using System.Drawing;

using MonoTouch.UIKit; using MonoTouch.Foundation;

namespace CurrentWeatherClock { public class CurrentWeatherClockViewController : UIViewController { private UILabel timeLabel; private NSTimer timer;

public override void ViewDidLoad() { base.ViewDidLoad(); this.View.BackgroundColor = UIColor.White;

this.timeLabel = new UILabel { Frame = new RectangleF(0, 180, this.View.Bounds.Width, 100), Text = “00:00:00 AM”, Font = UIFont.SystemFontOfSize(56) };

this.View.AddSubview (this.timeLabel); }

public override void ViewWillAppear (bool animated) { base.ViewWillAppear (animated);

this.UpdateClockView (); this.StartTimer (); }

private void UpdateClockView() { timeLabel.Text = String.Format (“{0:hh:mm:ss tt}”, DateTime.Now); }

private void StartTimer() { this.timer = NSTimer.CreateRepeatingScheduledTimer (TimeSpan.FromSeconds (1.0), delegate { this.UpdateClockView (); }); } } } [/code]

Stop the Timer

Stop the timer in the ViewWillDisappear method:

[code lang=text] public override void ViewWillDisappear (bool animated) { base.ViewWillDisappear (animated);

this.StopTimer (); }

private void StopTimer() { this.timer.Invalidate (); } [/code]

It’s a good idea to stop timers, tasks, and other continuously running resources that a page is using when the user navigates away.

Timer Periodicity

Let’s take an informal look at whether our timer really is firing at a one second interval. Change the clock display format:

[code lang=text] timeLabel.Text = String.Format (“{0:hh:mm:ss.ff}”, DateTime.Now); [/code]

Build and run. In my experiments, it looks pretty good on both the simulator and an iPhone device. However, let’s not take any chances. A common technique, when sampling a continuous fuction is to sample at twice the frequency1. The clock is not a true continuous function, but appears to be from our persepctive of displaying samples once per second.

Change the timer interval rate to one half second, then build and run again. Note that the clock is updated approximately every half second.

[code lang=text] private void StartTimer() { this.timer = NSTimer.CreateRepeatingScheduledTimer (TimeSpan.FromSeconds (0.5), delegate { this.UpdateClockView (); }); } [/code]

Cross-Platform Timer

This timer works fine, but when using Xamarin and C#, we’d like to use the .Net libraries where we can. This makes the app more cross-platform in case we wish to port to Android later. Of course, Android does not have a UIViewController, so that will have to change anyway. But in a larger application, you likely would have implemented the timer in a separate class that could be cross-platform.

Replace the timer field:

[code lang=text] private Timer timer;

[/code]

This will require another using statement:

[code lang=text] using System.Threading; [/code]

Replace the timer start and stop methods:

[code lang=text] private void StartTimer() { this.timer = new Timer(delegate {this.UpdateClockView();}, null, 0, 500); }

private void StopTimer() { if (this.timer != null) { this.timer.Change (Timeout.Infinite, Timeout.Infinite); } } [/code]

(If you’re used to Objective-C, you might be tempted to leave out the test for null. C# doesn’t work that way. You’ll get a null exception if you try it.)

Build and run. Oops, we got an exception anyway:

[code lang=text] MonoTouch.UIKit.UIKitThreadAccessException: UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread. [/code]

This happens because we’re updating the view on a thread other than the main thread. Fix it by invoking the assignment to the UILabel on the main thread:

[code lang=text] private void UpdateClockView() { InvokeOnMainThread (delegate { timeLabel.Text = String.Format (“{0:hh:mm:ss.ff}”, DateTime.Now); }); } [/code]

Tweak the Timer Start

This is great, but let’s do one more tweak to make the one second updates better match other clocks. Delay starting the timer to about the start of a new second:

[code lang=text] private void StartTimer() { int dueTime = 1000 - DateTime.Now.Millisecond; this.timer = new Timer(delegate {this.UpdateClockView();}, null, dueTime, 500); } [/code]

Restore AM/PM Indicator

And finally, let’s restore the AM/PM indicator:

[code lang=text] timeLabel.Text = String.Format (“{0:hh:mm:ss tt}”, DateTime.Now); [/code]

Final Version with Timer

[code lang=text] using System; using System.Drawing; using System.Threading;

using MonoTouch.UIKit; using MonoTouch.Foundation;

namespace CurrentWeatherClock { public class CurrentWeatherClockViewController : UIViewController { private UILabel timeLabel; private Timer timer;

public override void ViewDidLoad() { base.ViewDidLoad(); this.View.BackgroundColor = UIColor.White;

this.timeLabel = new UILabel { Frame = new RectangleF(0, 180, this.View.Bounds.Width, 100), Text = “00:00:00 AM”, Font = UIFont.SystemFontOfSize(56) };

this.View.AddSubview (this.timeLabel); }

public override void ViewWillAppear (bool animated) { base.ViewWillAppear (animated);

this.UpdateClockView (); this.StartTimer (); }

public override void ViewWillDisappear (bool animated) { base.ViewWillDisappear (animated);

this.StopTimer (); }

private void UpdateClockView() { InvokeOnMainThread (delegate { timeLabel.Text = String.Format (“{0:hh:mm:ss tt}”, DateTime.Now); }); }

private void StartTimer() { int dueTime = 1000 - DateTime.Now.Millisecond; this.timer = new Timer(delegate {this.UpdateClockView();}, null, dueTime, 500); }

private void StopTimer() { if (this.timer != null) { this.timer.Change (Timeout.Infinite, Timeout.Infinite); } } } } [/code]


  1. The Nyquist frequency, named after electronic engineer Harry Nyquist, is ½ of the sampling rate of a discrete signal processing system. http://en.wikipedia.org/wiki/Nyquist_frequency 

Updated: