2014-09-02 56 views
2

我目前正在Xamarin.ios中做一個小遊戲,其想法是您從按下開始到結束時都有30秒(並將您帶到遊戲結束屏幕),但如果你做錯了舉動,那麼它的遊戲結束了。我正在使用System.Timers並且定時器啓動正常,並開始滴答滴答,但是當您達到30秒並停止時,它會向控制檯輸出定時器已完成但不會執行任何我想要的操作。我嘗試了所有我能想到的東西,但似乎無法弄清楚。任何幫助表示讚賞,謝謝!也提前抱歉,這是我第一次使用stackoverflow。Xamarin.ios - 計時器不會在時間之後執行動作

下面是主要代碼:

//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

你可能執行的計時器在後臺線程(否則你UI線程會停止),但我沒有看到任何代碼確保Timer在UI線程上執行OnTimeEvent(因爲你在做UI的時候需要這個)。 你可以發佈你設置定時器的代碼嗎? – Frank 2014-09-02 08:33:59

+0

哦,對不起,我將添加代碼,然後結束的職位。這就是我在設置計時器方面的真實情況(對不起,我還在學習哈哈) – 2014-09-02 08:37:36

回答

3

的問題是定時器在後臺線程上執行。後臺線程無法修改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

非常感謝!這正是我正在尋找的,我只需要在InvokeOnMainThread(()=> {// stuff here})中包裝所有UI更新的東西; 工作很好! :) – 2014-09-02 09:28:36

3

只能在UI線程訪問UI組件。 OnTimeEvent從定時器的後臺線程調用,而不是UI線程。另一種解決方案,而不是使用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"); 
} 

然後只需調用運行();

await關鍵字有效地自動爲該方法的其餘部分自動設置一個繼續任務並立即返回。該方法的其餘部分在Task.Delay(30000)之後運行,但切換回UI上下文將自動處理爲await捕獲它。

+0

謝謝你的回答,這非常有幫助!我發現task.delay只能使用一次,然後在使用CancellationTokenSource時創建一些問題。但我想我可能有其他用途:)再次感謝! – 2014-09-02 09:30:38

+1

InvokeOnMainThread確實是iOS特有的。 您的解決方案是跨平臺的(並且可以說是更好的設計)。爲了簡單起見,如果開發人員不知道iOS和Android平臺中的線程,則InvokeOnMainThread或RunOnUIThread可能會更好。 雖然我已經遇到了Android線程處理TPL的一些特性。如果我必須結合使用ThreadPool和RunOnUIThread才能在正在運行的同時正常工作。 無論哪種方式,很好的例子! – Frank 2014-09-02 09:35:32

+0

「雖然我在Android線程處理TPL方面遇到了一些特殊問題,」哦,很棒的東西期待 – 2014-09-02 09:55:45

0

考慮您設置您在viewDidLoad中(定時器)(所以你不必在使用.net定時器任意代碼共享的好處),您也可以使用本機計時器只寫一行:

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

在你的情況,因爲後臺線程不能修改UI對象只是BeginInvokeOnMainThread包裝你的用戶界面的變化是這樣的:

DoYourStuffEveryTimeSpan() 
{ 
    BeginInvokeOnMainThread(() => 
     { 
     DoMagicUIChanges(); 
     }); 
    } 
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"); 
     }