Xamarin.ios - 计时器不会在一段时间后执行操作

时间:2014-09-02 08:31:08

标签: c# ios timer xamarin

我目前正在Xamarin.ios做一个小游戏,我的想法是你从按下开始到结束时有30秒(然后通过屏幕将你带到游戏中),但如果你做错了动作那么它也是游戏结束。我正在使用System.Timers并且计时器开始正常并开始滴答作响但是当你到达30秒并且它停止时,它会打印到控制台,计时器已经完成,但之后我没有做任何我想做的事情。我已经尝试了我能想到的一切,但似乎无法弄明白。任何帮助表示赞赏,谢谢!也提前抱歉,这是我第一次使用stackoverflow。

这是主要代码:

    //Setting up the timer
    int count = 1;
    System.Timers.Timer timer1;

    private void OnTimeEvent(object source, ElapsedEventArgs e)
    {
        count++;
        Console.WriteLine ("timer tick");
        if (count == 30)
        {
            timer1.Enabled = false;
            Console.WriteLine ("timer finished");

            //**Everything from here on is just what I want to happen**
            imgBackground.Image = UIImage.FromFile ("gameover.jpg");
            btn1.SetBackgroundImage (null, UIControlState.Normal);
            btn2.SetBackgroundImage (null, UIControlState.Normal);
            btn3.SetBackgroundImage (null, UIControlState.Normal);
            btn4.SetBackgroundImage (null, UIControlState.Normal);
            lblTime.Text = "";
            btnStart.Enabled = true;
            hasend = true;
            //score is set back to 0
            score = 0;
            btn1green = false;
            btn2green = false;
            btn3green = false;
            btn4green = false;
        }
    }

这是我在开始按钮上的内容(工作正常)

    //Start the timer
    timer1.Enabled = true;
    Console.WriteLine("timer started");

当你做出错误的举动时,这就是我所拥有的(也可以正常工作)

        else
        {
            //adjust the UI
            imgBackground.Image = UIImage.FromFile("gameover.jpg");
            btn1.SetBackgroundImage(null, UIControlState.Normal);
            btn2.SetBackgroundImage(null, UIControlState.Normal);
            btn3.SetBackgroundImage(null, UIControlState.Normal);
            btn4.SetBackgroundImage(null, UIControlState.Normal);
            lblTime.Text = "";
            btnStart.Enabled = true;
            hasend = true;
            //score is set back to 0
            score = 0;
            btn1green = false;
            btn2green = false;
            btn3green = false;
            btn4green = false;
            timer1.Enabled = false;
            Console.WriteLine("timer stopped");
        }

因为你可以看到,一旦计时器结束,这是一个相当大的改变UI。我认为这可能与它为什么不起作用有关

编辑:这也是我在ViewDidLoad()

下设置计时器的地方
timer1 = new System.Timers.Timer();
timer1.Interval = 1000;
timer1.Elapsed += new ElapsedEventHandler(OnTimeEvent);

4 个答案:

答案 0 :(得分:3)

问题是Timer是在后台线程上执行的。而后台线程无法修改UI对象。

timer1.Elapsed是从ViewDidLoad()中的UI线程设置的。但它将从后台线程调用。

解决方案是在InvokeOnMainThread(iOS)或RunOnUIThread(Android)中包装影响UI对象的代码。

有关InvokeOnMainThread的示例:http://developer.xamarin.com/guides/ios/user_interface/controls/part_2_-_working_with_the_ui_thread/

答案 1 :(得分:3)

您只能访问UI线程上的UI组件。从计时器的后台线程而不是UI线程调用OnTimeEvent。另一个解决方案而不是使用System.Timer和InvokeOnMainThread是使用Xamarin支持的TPL。这也适用于android和winphone(我认为InvokeOnMainThread是特定于ios的吗?)

TaskScheduler uiContext = TaskScheduler.FromCurrentSynchronizationContext();
Console.WriteLine("timer started");
Task.Delay(30000).ContinueWith((task) =>
    {
        //Do UI stuff
        label.Text = "!!";
        Console.WriteLine("timer stopped");
    }, uiContext);

Task.Delay(30000)启动并返回一个需要30秒才能完成的后台任务。 ContinuesWith()调用提供了一个在该任务完成后运行的方法,一个延续任务。为了确保继续任务在UI线程上运行,传入了TaskScheduler。这个TaskScheduler来自UI线程。

比你更简单,因为你使用的是C#,你可以使用内置的语言支持来写更短的内容:

async Task Run()
{
    Console.WriteLine("timer started");
    await Task.Delay(30000);
    //Do UI stuff
    label.Text = "!!";
    Console.WriteLine("timer stopped");
}

然后只需调用Run();

await关键字有效地自动为方法的其余部分设置延续任务并立即返回。该方法的其余部分在Task.Delay(30000)之后运行,但切换回UI上下文时会自动处理,等待捕获它。

答案 2 :(得分:0)

考虑到您在ViewDidLoad()中设置计时器(因此您在使用.Net计时器时没有任何共享代码优势),您也可以使用原生计时器只写一行:

NSTimer timer = NSTimer.CreateScheduledTimer(TimeSpan.FromSeconds(30), delegate { DoYourStuffEveryTimeSpan(); });

在您的情况下,由于后台线程无法修改UI对象,只需使用BeginInvokeOnMainThread包装UI更改,如下所示:

 DoYourStuffEveryTimeSpan()
 {
   BeginInvokeOnMainThread(() =>
      {
        DoMagicUIChanges();
      });
  }

答案 3 :(得分:0)

        var timer = new Timer(2000);
        timer.Elapsed += OnTimerElapsed;
        timer.Start ();
        Console.WriteLine ("Timer started, control is back here");


void OnTimerElapsed(object sender, EventArgs e)
        {
            Console.WriteLine ("Time Elapsed");
        }