共用方式為


本文章是由機器翻譯。

Windows Phone

為 Windows Phone 與 iOS 建置應用程式

Andrew Whitechapel

下載代碼示例

有很多有關移植到 Windows Phone,從 iOS 應用程式檔,但在本文中,我開始的前提是您要生成的目標的從零開始的新應用程式這兩個平臺。 我沒有價值判斷哪種平臺是更好。 相反,我建立 app,採取務實的做法,並描述在路上遇到兩個平臺的異同。

作為 Windows Phone 團隊的成員,我熱愛的 Windows Phone 平臺 — — 但我在這裡的關鍵點不是一個平臺是優於其他,但該平臺是不同的需要一些不同的程式設計方法。 雖然您可以開發 iOS 軟體在 C# 中,使用 MonoTouch 系統,這是一個少數群體環境。 對於本文中,我使用標準 Xcode 和目標 C iOS 和 Visual Studio 和 C# Windows Phone。

目標使用者體驗

我的目標是要實現兩個版本的同時確保一如既往模型,每個版本的應用程式和哲學的目標平臺的同一 UX。 為了說明我的意思,請考慮 Windows Phone 版本的應用程式實現的主要使用者介面與垂直捲動清單方塊,而 iOS 版本可實現橫向 ScrollViewer 相同的使用者介面)。 很明顯,這些差異是不僅僅是軟體 — — 就是我能建造 iOS 中的垂直捲動清單或水準滾動清單中 Windows Phone。 迫使這些首選項將會較少對各自的設計理念,不過,和我想要避免這種"非自然的行為"。

App,SeaVan,顯示四個土地邊界過境點之間在美國西雅圖和溫哥華、 英屬哥倫比亞,在加拿大,在每個不同的過境通道的等待時間。 應用程式從這兩個美國在通過 HTTP 提取資料 加拿大政府 Web 網站並刷新的資料可以通過一個按鈕手動或自動通過計時器。

圖 1 介紹了兩種實現方式。 您會注意到的一個區別是 Windows Phone 版本主題意識到並使用當前的強調文字顏色。 相比之下,iOS 版本不會有一個主題或強調文字顏色的概念。

The Main UI Screen for the SeaVan App on an iPhone and a Windows Phone Device
圖 1 主 UI 螢幕在 iPhone 上的 App SeaVan 和 Windows Phone 設備

Windows Phone 設備有嚴格的線性基於頁面的導航模型。 使用者介面作為一個頁,提出的所有重要的螢幕和使用者將向前和向後導航到頁面堆疊。 您可以實現相同的線性導航在 iPhone 上,但 iPhone 並不會受到此模型,所以你可以自由地適用任何螢幕模式你喜歡。 在 SeaVan 的 iOS 版本,在輔助螢幕約為模態視圖控制器。 從技術角度來看,這些是大致相當於 Windows Phone 模態快顯視窗。

圖 2 介紹了示意圖的廣義的 ui,與除­宇白和外部使用者介面元素中的 UI 元素 (發射器 /­挑肥揀瘦在共用的應用程式在 iOS Windows Phone) 橙色。 設置 (中淺綠色) 的使用者介面是一種反常的現象,我將在本文稍後部分進行描述。

Generalized Application UI
圖 2 的廣義應用程式使用者介面

使用者介面的另一個區別是 Windows Phone 使用 ApplicationBar 作為一個標準化的 UI 元素。 在 SeaVan,此欄是使用者從何處查找按鈕來調用在應用程式中的協助工具 — — 關於頁面和設置頁面 — — 和手動刷新資料。 沒有直接的 iOS 相當於 ApplicationBar,所以在 iOS 版本的 SeaVan,一個簡單的工具列提供了等效的 ux 選項。

相反,iOS 版本具有 PageControl — — 帶有四個定位點指標的螢幕底部的黑色欄。 使用者可以通過四個邊界過境點,通過涮內容本身或通過點擊 PageControl 水準滾動。 在 Windows Phone SeaVan,沒有 PageControl 等效。 相反,Windows Phone SeaVan 使用者滾動的過境通過直接刷內容。 使用 PageControl 的後果之一是它是容易配置,以便每個頁面停靠和完全可見。 滾動清單方塊的 Windows Phone,沒有標準的支援,為此,因此使用者可以最後的兩個邊界過境點的局部視圖。 ApplicationBar 和 PageControl 是那裡沒有試圖使 UX 跨比它的任何更多制服可以只需使用標準的行為的兩個版本的示例。

體系結構的決定

使用模型-視圖-ViewModel (MVVM) 體系結構被鼓勵這兩個平臺。 一個區別是 Visual Studio 生成代碼,包括提述主模型中的應用程式物件。 Xcode 不這樣做 — — 你可以自由地你 viewmodel 插入您的應用程式,只要您選擇。 在這兩種平臺,以插入到應用程式物件的模型是有意義的。

更重要的區別是的模型資料通過在視圖模型流動的機制。 在 Windows Phone,這通過資料繫結,它允許您在 XAML 中指定 UI 元素的關聯模型資料的方式 — — 和運行時照顧實際上將值傳播。 在 iOS,雖然有協力廠商庫,提供類似行為 (基於鍵-值觀察者模式),沒有資料繫結等效標準 iOS 庫中。 相反,應用程式必須手動傳播模型和視圖之間的資料值。 圖 3 闡釋了廣義的體系結構和元件的 SeaVan,與 viewmodels 粉色和藍色的意見。

Generalized SeaVan Architecture
圖 3 的廣義 SeaVan 體系結構

目標 C 和 C#

目標 C 和 C# 的詳細的比較顯然超出了一篇短文的範圍,但圖 4 提供的關鍵構造近似映射。

圖中的目標 C 及對應的 C# 4 關鍵構造

目標 C 概念 C# 對應
美孚 @interface:酒吧 {} 類聲明中,包括繼承 美孚類:酒吧 {}

美孚 @implementation

@end

類的實現 美孚類:酒吧 {}
美孚 * f = [[美孚頁頭] init] 類具現化和初始化 美孚 f = 新 Foo() ;
-(void) doSomething {} 實例方法聲明 不正確 doSomething() {}
+(void) {doOther} 類方法聲明 靜態 void doOther() {}

[myObject doSomething] ;

myObject.doSomething

發送一條消息 (上調用的方法) 物件 myObject.doSomething()
[自我助學金] 發送一條消息 (上調用的方法) 的當前物件 this.doSomething()
-(id) init {} 初始值設定項 (建構函式) Foo() {}
-(id) initWithName:(NSString*) n 價格: (int) p {} 帶參數的初始值設定項 (建構函式) 美孚 (字串 n,int p) {}
@property NSString * 名稱 ; 屬性聲明 公共字串名稱 {獲取 ; 設置 ; }
美孚 @interface: NSObject <UIAlertViewDelegate> 美孚子類 NSObject 和 UIAlertViewDelegate 實現協定 (大約相當於 C# 介面) 美孚類: IAnother

核心應用程式元件

若要啟動 SeaVan 應用程式,創建 Xcode 及其在 Visual Studio 中的 Windows Phone 應用新的單一視圖應用程式。 這兩種工具將生成一組的起始檔,包括表示應用程式物件的主頁面或視圖類的專案。

IOS 公約是在類名稱,使用兩個字母首碼,所以 SeaVan 的所有自訂類的首碼為"SV。"IOS 應用程式開始與平常 C 主要的方法,創建應用程式的委託。 在 SeaVan,這是 SVAppDelegate 類的一個實例。 App 委託相當於 Windows Phone 中的應用程式物件。 我在與自動引用計數 (弧) 打開 Xcode 中創建專案。 這將周圍的所有代碼 @autoreleasepool 範圍聲明添加在 main 中,如下所示:

int main(int argc, char *argv[])
{
  @autoreleasepool {
    return UIApplicationMain(
    argc, argv, nil, 
    NSStringFromClass([SVAppDelegate class]));
  }
}

系統將現在自動引用計數的物件創建,並自動釋放他們時,計數變為零。 @Autoreleasepool 整齊地照顧大多數常見的 C/c + + 記憶體管理問題,使更接近的程式設計體驗 C#。

在 SVAppDelegate 介面聲明中,指定它是 <UIApplicationDelegate>。 這意味著它回應標準應用程式消息的委託,如應用程式: didFinishLaunchingWith­選項。 我還宣佈 SVContentController 屬性。 在 SeaVan,這對應于 Windows Phone 中的標準首頁類。 最後一個屬性是一個 SVBorderCrossings 指標 — — 這是我主要的模型,將各自代表一個過境舉行 SVBorderCrossing 項的集合:

@interface SVAppDelegate : UIResponder <UIApplicationDelegate>{}
@property SVContentController *contentController;
@property SVBorderCrossings *border;
@end

當主啟動初始化應用程式委託和系統發送它應用程式消息選擇器 didFinishLaunching 與­WithOptions。 比較這 Windows Phone,那裡的邏輯等價物將應用程式啟動或活性炭事件處理常式。 在這裡,載入 Xcode 介面產生器 (XIB) 檔命名的 SVContent 並使用它來初始化我主視窗。 Windows Phone 對應于一個 XIB 檔是一個 XAML 檔。 XIB 檔實際上是 XML 檔,儘管您通常它們間接使用 Xcode 圖形 XIB 編輯器編輯 — — 類似于在 Visual Studio 中的圖形 XAML 編輯器。 我 SVContentController 類是關聯的 SVContent.xib 檔,Windows Phone 首頁類是與 MainPage.xaml 檔相關聯的方式相同。

最後,我 SVBorderCrossings viewmodel 具現化和調用其初始值設定項。 未在 iOS,您通常分配和初始化一個語句,以避免潛在缺陷的使用中初始化的物件:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [[NSBundle mainBundle] loadNibNamed:@"SVContent" 
    owner:self options:nil];
  [self.window addSubview:self.contentController.view];
  border = [[SVBorderCrossings alloc] init];
  return YES;
}

在 Windows Phone 的 XIB 載入操作的對應通常為您在幕後完成。 生成生成的代碼,使用 XAML 檔的這一部分。 例如,如果您取消隱藏 Obj 資料夾中的隱藏的檔,在 MainPage.g.cs 中你會看到的 InitializeComponent 方法,將物件的 XAML 載入:

public partial class MainPage : Microsoft.Phone.Controls.PhoneApplicationPage
{
  public void InitializeComponent()
  {
    System.Windows.Application.LoadComponent(this,
      new System.Uri("/SeaVan;component/MainPage.xaml",
      System.UriKind.Relative));
  }
}

SVContentController 類作為我主頁面將主辦滾動檢視器,反過來將舉辦四個視圖控制器。 每個視圖控制器將最終將填充資料從四個西雅圖溫哥華邊境口岸之一。 我宣佈要將 <UIScrollViewDelegate> 的類 並定義三個屬性:UIScrollView、 UIPageControl 和 NSMutableArray 查看控制器。 局部和 pageControl 都聲明為 IBOutlet 的屬性,它允許我將它們連接到 XIB 中的 UI 工件編輯器。 XAML 中的元素用 x: 名稱,生成的類欄位聲明時,Windows Phone 中的等效。 在 iOS,你也可以連接 XIB UI 元素到 IBAction 屬性在類中,使您可以掛鉤 UI 事件。 Silverlight 等效是添加時,說,按一下在 XAML 中將事件掛鉤,並提供存根 (stub) 的代碼的類中的事件處理常式處理常式。 有趣的是,我的 SVContentController 不會子類的任何 UI 類。 相反,它的子類 NSObject 類的基類。 它作為 SeaVan 中的 UI 元素,因為它可以實現 <UIScrollViewDelegate> 議定書 》 — — 也就是說,它回應局部的消息:

@interface SVContentController : NSObject <UIScrollViewDelegate>{}
@property IBOutlet UIScrollView *scrollView;
@property IBOutlet UIPageControl *pageControl;
@property NSMutableArray *viewControllers;
@end

在 SVContentController 執行時,第一種方法來調用是 awakeFromNib (NSObject 從繼承)。 在這裡,創建 SVViewController 物件的陣列,並將每個頁面的視圖添加到局部:

- (void)awakeFromNib
{   
  self.viewControllers = [[NSMutableArray alloc] init];
  for (unsigned i = 0; i < 4; i++)
  {
    SVViewController *controller = [[SVViewController alloc] init];
    [controllers addObject:controller];
    [scrollView addSubview:controller.view];
  }
}

最後,當使用者刷局部或點擊頁面控制項時,得到 scrollViewDidScroll 消息。 在此方法中,一半以上的上一個/下一個頁面可見時切換 PageControl 指示器。 然後我載入可見的頁面,加上任意一側的 (以避免閃爍,當使用者開始滾動) 的頁。 我在這兒做的最後一件事是調用的私有方法,updateViewFromData,其中中提取模型的資料,並手動將它設置成在 UI 中的每個欄位:

- (void)scrollViewDidScroll:(UIScrollView *)sender
{
  CGFloat pageWidth = scrollView.frame.size.width;
  int page = floor((scrollView.contentOffset.x - 
    pageWidth / 2) / pageWidth) + 1;
  pageControl.currentPage = page;
  [self loadScrollViewWithPage:page - 1];
  [self loadScrollViewWithPage:page];
  [self loadScrollViewWithPage:page + 1];
  [self updateViewFromData];
}

在 Windows Phone,在首頁,以聲明方式在 XAML 中實現相應的功能。 顯示時間使用 TextBlock 控制項內的清單方塊 DataTemplate 過境。 清單方塊中每個資料集到視圖自動滾動,所以 Windows Phone SeaVan 有沒有自訂的代碼,以處理滾動筆勢。 沒有對應的 updateViewFromData 方法,因為該操作是照顧通過資料繫結。

提取和分析 Web 資料

作為應用程式委託運作,以及 SVAppDelegate 類中聲明的欄位和屬性,以支援提取和分析從美國過境資料 和加拿大的 Web 網站。 我宣佈兩個的 NSURLConnection 欄位,這兩個網站的 HTTP 連接。 我還宣佈了兩個 NSMutableData 欄位 — — 我將使用要追加每個資料塊和進來的緩衝區。 更新類來實現 <NSXMLParserDelegate> 協定,所以也是標準的應用程式的委託,它也是 XML 分析器委託。 當收到的 XML 資料時,此類將首先調用來分析它。 因為我知道我會處理兩個完全不同的 XML 資料集,我就會立即手掉的工作,向兩名兒童分析器代表之一。 我宣佈一對自訂 SVXMLParserUs /­SVXMLParserCa 為此欄位。 該類還聲明一個計時器,自動刷新功能。 對於每個計時器事件,我就會調用 refreshData 方法,如中所示圖 5

圖 5 介面聲明為 SVAppDelegate 的

@interface SVAppDelegate : 
  UIResponder <UIApplicationDelegate, NSXMLParserDelegate>
{
  NSURLConnection *connectionUs;
  NSURLConnection *connectionCa;
  NSMutableData *rawDataUs;
  NSMutableData *rawDataCa;
  SVXMLParserUs *xmlParserUs;
  SVXMLParserCa *xmlParserCa;
  NSTimer *timer;
}
@property SVContentController *contentController;
@property SVBorderCrossings *border;
- (void)refreshData;
@end

RefreshData 方法為每個傳入的資料集分配可變數據的緩衝區,並建立了兩個 HTTP 連接。 我正在使用 SVURLConnectionWithTag 的自訂類,子類 NSURLConnection 因為 iOS 分析器的委託模型必須關閉這兩個請求從相同的物件,踢和所有資料會都回來到此物件。 所以我需要一種方法來區分,美國 和加拿大進來的資料。 要這樣做,我只是將標記附加到每個連接和緩存中 NSMutableDictionary 這兩個連接。 當初始化每個連接時,指定自我的委託。 每當收到的資料區塊、 調用 connectionDidReceiveData 方法,和我實現這要將資料追加到該標記緩衝區 (見圖 6)。

圖 6 設置 HTTP 連接

static NSString *UrlCa = @"http://apps.cbp.gov/bwt/bwt.xml";
static NSString *UrlUs = @"http://wsdot.wa.gov/traffic/rssfeeds/CanadianBorderTrafficData/Default.aspx";
NSMutableDictionary *urlConnectionsByTag;
- (void)refreshData
{
  rawDataUs = [[NSMutableData alloc] init];
  NSURL *url = [NSURL URLWithString:UrlUs];
  NSURLRequest *request = [NSURLRequest requestWithURL:url];   
  connectionUs =
  [[SVURLConnectionWithTag alloc]
    initWithRequest:request
    delegate:self
    startImmediately:YES
    tag:[NSNumber numberWithInt:ConnectionUs]];
    // ...
Code omitted: set up the Canadian connection in the same way
}

我還必須實現 connectionDidFinishLoading。 當收到的所有資料時 (對於這兩個連接之一) 時,作為第一個解析器設置此應用程式委託物件。 解析消息是阻止調用,所以當它返回時,我可以在我內容的控制器,以更新 UI 的已解析的資料調用 updateViewFromData:

- (void)connectionDidFinishLoading:(SVURLConnectionWithTag *)connection
{
  NSXMLParser *parser = 
    [[NSXMLParser alloc] initWithData:
  [urlConnectionsByTag objectForKey:connection.tag]];
  [parser setDelegate:self];
  [parser parse];
  [_contentController updateViewFromData];
}

一般情況下,有兩種類型的 XML 解析器:

  • 隨著分析器將遍歷 XML 樹,將通知您代碼的 XML (SAX) 解析器的簡單的 API
  • 文件物件模型 (DOM) 分析器,其中讀取整個文檔,並建立記憶體中表示形式,您可以查詢不同元素

NSXMLParser iOS 中的預設為 SAX 解析器。 協力廠商 DOM 解析器可供使用的 iOS,但我想要比較標準的平臺,而不是訴諸于協力廠商庫。 標準語法分析器反過來工程通過每個元素,並有沒有了解當前項適合整體的 XML 文檔中的位置。 出於此原因,在 SeaVan 中的父解析器處理它關心的最外層塊,然後關閉到孩子的手將委託分析器來處理下一個內部塊。

在解析器委託方法中,我做一個簡單的測試,來區分,美國 來自加拿大的 XML 中,XML 具現化相應子解析器,並設置這孩子是從此時當前解析器。 我還設置兒童的父分析器的自我使兒童可以回父返回解析控制項時它獲取到的 XML,它可以處理的末尾 (見圖 7)。

圖 7 分析器委託方法

- (void)connection:(SVURLConnectionWithTag *)connection didReceiveData:(NSData *)data
{
  [[urlConnectionsByTag objectForKey:connection.tag] appendData:data];
}
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
  namespaceURI:(NSString *)namespaceURI
  qualifiedName:(NSString *)qName
  attributes:(NSDictionary *)attributeDict
{
  if ([elementName isEqual:@"rss"]) // start of US data
  {
    xmlParserUs = [[SVXMLParserUs alloc] init];
    [xmlParserUs setParentParserDelegate:self];
    [parser setDelegate:xmlParserUs];
  }
  else if ([elementName isEqual:@"border_wait_time"]) // start of Canadian data
  {
    xmlParserCa = [[SVXMLParserCa alloc] init];
    [xmlParserCa setParentParserDelegate:self];
    [parser setDelegate:xmlParserCa];
  }
}

等效的 Windows Phone 代碼,我首先設置 Web 請求美國 Web 網站並為加拿大的 Web 網站。 在這裡我使用 WebClient 即使區域性往往是更適當的最優的性能和回應能力。 我成立 OpenReadCompleted 事件的處理常式,然後非同步打開請求:

public static void RefreshData()
{
  WebClient webClientUsa = new WebClient();
  webClientUsa.OpenReadCompleted += webClientUs_OpenReadCompleted;
  webClientUsa.OpenReadAsync(new Uri(UrlUs));
  // ...
Code omitted: set up the Canadian WebClient in the same way
}

在每個請求 OpenReadCompleted 事件處理常式中中, 提取的資料,已作為流物件返回並把它傳遞給一個説明器物件來解析 XML。 因為我有兩個獨立的 Web 請求和兩個獨立的 OpenReadCompleted 事件處理常式,我不需要請求或做任何測試,以確定哪個請求任何特定的傳入資料屬於添加標籤。 我也不需要處理每個傳入的建立整體的 XML 文檔的資料塊。 相反,我可以坐視不理和等待,直到收到的所有資料:

private static void webClientUs_OpenReadCompleted(object sender, 
  OpenReadCompletedEventArgs e)
{
  using (Stream result = e.Result)
  {
    CrossingXmlParser.ParseXmlUs(result);
  }
}

用於解析 XML,相比之下到 iOS,Silverlight 包括一個 DOM 解析器作為標準,XDocument 類表示。 所以不必解析器的層次結構,我可以使用 XDocument 直接來做所有的解析工作:

internal static void ParseXmlUs(Stream result)
{
  XDocument xdoc = XDocument.Load(result);
  XElement lastUpdateElement = 
    xdoc.Descendants("last_update").First();
  // ...
Etc.
}

支援意見和服務

在 Windows Phone 應用程式物件的靜態,並可用於在應用程式中的任何其他元件。 同樣,在 iOS,一個 UIApplication 委託類型是在應用程式中可用。 以輕鬆的事情,我在應用程式中進行適當地轉換為特定的 SVAppDelegate 類型的委託應用程式定義的宏,可以使用任意位置:

#define appDelegate ((SVAppDelegate *) [[UIApplication sharedApplication] delegate])

這允許我,例如,當使用者點擊刷新按鈕時調用的 app 委託 refreshData 方法 — — 一個屬於我的視圖控制器的按鈕:

- (IBAction)refreshClicked:(id)sender
{
  [appDelegate refreshData];
}

當使用者點擊關於按鈕時,我希望顯示關於) 螢幕,如中所示圖 8。 在 iOS,我具現化 SVAboutViewController,在工具列中有關聯的 XIB,使用者指南中,滾動文本元素,以及三個其他按鈕。

The SeaVan About Screen in iOS and Windows Phone
圖 8 SeaVan 關於螢幕在 iOS 和 Windows Phone

若要顯示此視圖控制器,我其具現化併發送當前物件 (自我) presentModalViewController 的消息:

- (IBAction)aboutClicked:(id)sender
{
  SVAboutViewController *aboutView =
    [[SVAboutViewController alloc] init];
  [self presentModalViewController:aboutView animated:YES];
}

我謹此陳在 SVAboutViewController 類中,實現取消按鈕來關閉此視圖控制器,使還原為調用視圖控制器的控制:

- (IBAction) cancelClicked:(id)sender
{
  [self dismissModalViewControllerAnimated:YES];
}

這兩個平臺提供了一個應用程式調用中內置的應用程式,如電子郵件、 電話和短信功能的標準方式。 主要的區別在於是否控制將返回到應用程式後的內置功能返回,總是發生在 Windows Phone 中。 IOS,在這種情況對於某些功能而不是其他。

在 SVAboutViewController,當使用者點擊支援按鈕時我想撰寫電子郵件,使用者可以發送給開發團隊。 MFMailComposeViewController — — 作為一個模態視圖再次提出 — — 非常適合這一目的。 此標準視圖控制器還實現了取消按鈕,不會完全相同的工作要解雇本身並還原到其調用視圖控制項:

- (IBAction)supportClicked:(id)sender
{
  if ([MFMailComposeViewController canSendMail])
  {
    MFMailComposeViewController *mailComposer =
      [[MFMailComposeViewController alloc] init];
    [mailComposer setToRecipients:
      [NSArray arrayWithObject:@"tensecondapps@live.com"]];
    [mailComposer setSubject:@"Feedback for SeaVan"];
    [self presentModalViewController:mailComposer animated:YES];
}

IOS 中獲取地圖方向的標準方式是調用谷歌地圖。 這種方法的缺點是它會將使用者到 (內置應用程式),Safari 共用應用程式和有沒有辦法控制以程式設計方式返回給應用程式。 我想要最小化的地方,在使用者離開應用程式,所以方向,而我提出目標的過境地圖通過使用自訂的 SVMapViewController 承載標準的 MKMapView 控制項:

- (IBAction)mapClicked:(id)sender
{   
  SVBorderCrossing *crossing =
    [appDelegate.border.crossings
    objectAtIndex:parentController.pageControl.currentPage];
  CLLocationCoordinate2D target = crossing.coordinatesUs;
  SVMapViewController *mapView =
    [[SVMapViewController alloc]
    initWithCoordinate:target title:crossing.portName];
  [self presentModalViewController:mapView animated:YES];
}

若要允許使用者輸入審查,我可以撰寫在 iTunes 應用程式商店中應用程式的連結。 (在下面的代碼中的九位 ID 是應用程式的應用程式商店 id。)我將這給 Safari 瀏覽器 (共用的應用程式)。 我有沒有留 app 的選項:

- (IBAction)appStoreClicked:(id)sender
{
  NSString *appStoreURL =
    @"http://itunes.apple.com/us/app/id123456789?mt=8";
  [[UIApplication sharedApplication]
    openURL:[NSURL URLWithString:appStoreURL]];
}

Windows Phone 相當於關於按鈕是 ApplicationBar 上的一個按鈕。 當使用者點擊此按鈕時,調用 NavigationService 以導航到 AboutPage:

private void appBarAbout_Click(object sender, EventArgs e)
{
  NavigationService.Navigate(new Uri("/AboutPage.xaml", 
    UriKind.Relative));
}

一樣的 iOS 版本,AboutPage 提供了在滾動文字的簡單的使用者指南。 沒有取消按鈕,因為使用者可以點擊硬體後退按鈕回到從這頁導航。 而不是支援和 App Store 按鈕,我有超連結­按鈕控制項。 支援電子郵件,我可以通過使用指定的 mailto NavigateUri 以聲明的方式實現的行為:協定。 這是不足以調用 EmailComposeTask:

<HyperlinkButton 
  Content="tensecondapps@live.com" 
  Margin="-12,0,0,0" HorizontalAlignment="Left"
  NavigateUri="mailto:tensecondapps@live.com" 
  TargetName="_blank" />

與按一下在代碼中,處理常式設置評論連結,然後調用 MarketplaceReviewTask 發射裝置:

private void ratingLink_Click(object sender, 
  RoutedEventArgs e)
{
  MarketplaceReviewTask reviewTask = 
    new MarketplaceReviewTask();
  reviewTask.Show();
}

早在首頁,而不是提供一個單獨的按鈕對於地圖/行車路線功能,我實施 SelectionChanged 事件清單方塊中的使用者可以點擊要調用此功能的內容。 這種做法是符合 Windows 存儲應用程式,使用者應該相互作用直接與內容而不是通過鉻元素間接的。 在此處理程式中,所點燃的 BingMapsDirectionsTask 發射裝置:

private void CrossingsList_SelectionChanged(
  object sender, SelectionChangedEventArgs e)
{
  BorderCrossing crossing = (BorderCrossing)CrossingsList.SelectedItem;
  BingMapsDirectionsTask directions = new BingMapsDirectionsTask();
  directions.End =
    new LabeledMapLocation(crossing.PortName, crossing.Coordinates);
  directions.Show();
}

應用程式設定

在 iOS 平臺上,應用程式首選項提供的使用者介面來編輯內置和協力廠商應用程式的設置該內置設置應用程式的集中管理。 圖 9 顯示主要設置 UI 和特定的 SeaVan 設置視圖中 iOS 和 Windows Phone 設置頁。 有一個設置為 SeaVan — — 切換為自動刷新功能。

Standard Settings and SeaVan-Specific Settings on iOS and the Windows Phone Settings Page
圖 9 標準設定和 SeaVan 具體設置在 iOS 和 Windows Phone 設置頁上

要將設置在應用程式內的,我使用 Xcode 創建特殊類型的資源,稱為一個設置束。 然後使用 Xcode 設置編輯器配置設置值 — — 沒有所需的代碼。

在應用程式方法中,所示圖 10,我確保設置同步,然後從存儲區讀取的當前值。 如果自動刷新設置值為 True,啟動計時器。 Api 支援獲取和設置在應用程式內的值,所以我可以選擇提供在應用程式中除了在應用程式中設置的應用程式的視圖設置視圖。

圖 10 中的應用方法

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSUserDefaults *defaults =
    [NSUserDefaults standardUserDefaults];
  [defaults synchronize];
  boolean_t isAutoRefreshOn =
    [defaults boolForKey:@"autorefresh"];
  if (isAutoRefreshOn)
  {
    [timer invalidate];
    timer =
      [NSTimer scheduledTimerWithTimeInterval:kRefreshIntervalInSeconds
        target:self
        selector:@selector(onTimer)
        userInfo:nil
        repeats:YES];
  }
  // ...
Code omitted for brevity
  return YES;
}

在 Windows Phone 中無法添加到全域設置應用程式的應用程式設定。 相反,我向我自己設置使用者介面應用程式中。 在 SeaVan,隨著 AboutPage,SettingsPage 是只是另一頁。 我在 ApplicationBar 導航到此頁面上提供一個按鈕:

private void appBarSettings_Click(object sender, 
  EventArgs e)
{
  NavigationService.Navigate(new Uri("/SettingsPage.xaml", 
    UriKind.Relative));
}

在 SettingsPage.xaml,我定義為自動刷新功能的 ToggleSwitch:

<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <toolkit:ToggleSwitch
    x:Name="autoRefreshSetting" Header="auto-refresh"
    IsChecked="{Binding Source={StaticResource appSettings},
    Path=AutoRefreshSetting, Mode=TwoWay}"/>
</StackPanel>

我別無選擇,但若要提供設置在應用程式內的行為,但可以關閉此向我的優勢為它實施 AppSettings viewmodel,並將其掛鉤到通過資料繫結視圖,就像處理任何其他資料模型一樣。 在首頁類中,我開始關閉此設置的值基於計時器:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  if (App.AppSettings.AutoRefreshSetting)
  {
    timer.Tick += timer_Tick;
    timer.Start();
  }
}

版本說明和應用程式範例

平臺版本:

  • Windows Phone SDK 7.1 和 Windows Phone 工具組 Silverlight
  • iOS 5 和 Xcode 4

SeaVan 將發佈到 Windows Phone 市場和 iTunes 軟體商店。

不上難

建設一個應用程式的目標是這兩個 iOS 和 Windows Phone 並不是那麼困難:相似之處均大於分歧。 兩者都與應用程式的物件和一個或多個頁/視圖物件,使用 MVVM 和 UI 類關聯 XML (XAML 或 XIB),與一個圖形化編輯器進行編輯。 在 iOS,您發送消息到一個物件,而在 Windows Phone 調用物件上的方法。 但區別是,我幾乎是學術性的和您甚至可以使用點標記法在 iOS 中如果你不喜歡的"[消息]"標記法。 這兩種平臺有事件/委託機制、 實例和靜態方法、 私人和公共成員和使用 get 和 set 訪問器的屬性。 在這兩個平臺上,可以調用內置應用程式的功能,並支援使用者設置。 顯然,你要保持兩個基本代碼 — — 但您的應用程式體系結構、 主要元件設計和使用者體驗可以舉行一致跨平臺。 試試 — — 你就會有意外的驚喜 !

Andrew Whitechapel 已超過 20 多年開發人員和目前工作作為專案經理 Windows Phone 團隊,負責應用平臺的核心部分。他的新書是"Windows Phone 7 發展內核"(微軟出版社,2012年)。

由於下面的技術專家對本文的審閱:鐘韋伯斯特和傑夫 · 威爾科克斯