我目前正在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);
答案 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");
}