在 Xamarin.iOS 的程式碼中建立 iOS 使用者介面
iOS 應用程式的使用者介面就像店面 -- 應用程式通常會取得一個 Window,但它可以填滿視窗,視應用程式想要顯示的內容而定,可以將視窗填滿盡可能多的物件,以及對象和排列方式。 此案例中的物件 (使用者所看見的內容) 稱為「檢視」。 為了在應用程式中建置單一畫面,檢視會在「內容檢視階層」中彼此堆疊,並由單一檢視控制器管理該階層。 具有多個畫面的應用程式會有多個內容檢視階層,每個都有它自己的檢視控制器,而應用程式會將檢視放置於視窗中,以根據使用者所在畫面建立不同的內容檢視階層。
下圖說明視窗、檢視、子檢視及檢視控制器之間的關聯性,以在裝置畫面中顯示使用者介面:
您可以使用 Xcode 的 Interface Builder 來建構這些檢視階層,不過,對於如何在程式碼中完全運作有基本瞭解是很好的。 本文會逐步解說一些基本要點,以使用僅限程式代碼的使用者介面開發來啟動並執行。
建立僅限程式代碼的專案
iOS 空白專案範本
首先,使用檔案新專案 > Visual C# > i 電話 和 iPad > iOS 應用程式 (Xamarin) 專案在 Visual Studio 中建立 iOS > 專案,如下所示:
然後選擇[ 空白應用程式 ] 項目樣本:
空白專案範本會將 4 個檔案新增至專案:
- AppDelegate.cs - 包含
UIApplicationDelegate
子類別,AppDelegate
用來處理來自 iOS 的應用程式事件。 應用程式視窗會在的FinishedLaunching
方法中AppDelegate
建立。 - Main.cs - 包含應用程式的進入點,指定的
AppDelegate
類別。 - Info.plist - 包含應用程式組態資訊的屬性清單檔案。
- Entitlements.plist – 屬性清單檔案,其中包含應用程式功能和許可權的相關信息。
iOS 應用程式是使用 MVC模式所建置。 應用程式顯示的第一個畫面是從視窗的根檢視控制器建立。 如需MVC模式本身的詳細資訊,請參閱 Hello、iOS 多螢幕指南。
範本所新增的 AppDelegate
實作會建立應用程式視窗,其中每個 iOS 應用程式只有一個,並透過下列程式代碼顯示:
public class AppDelegate : UIApplicationDelegate
{
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
如果您現在要執行此應用程式,可能會擲回指出 的 Application windows are expected to have a root view controller at the end of application launch
例外狀況。 讓我們新增控制器,並將其設為應用程式的根檢視控制器。
新增控制器
您的應用程式可以包含許多檢視控制器,但它需要有一個根檢視控制器來控制所有檢視控制器。 藉由建立 UIViewController
實例並將控制器設定為 屬性,將控制器新增至 Window.RootViewController
視窗:
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new UIViewController();
controller.View.BackgroundColor = UIColor.LightGray;
Window.RootViewController = controller;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
每個控制器都有相關聯的檢視,可從屬性存取 View
。 上述程式代碼會將檢視的 BackgroundColor
屬性 UIColor.LightGray
變更為 ,使其可見,如下所示:
我們也可以以這種方式設定任何 UIViewController
子類別,包括來自UIKit的控制器,以及我們自行撰寫的子類別 RootViewController
。 例如,下列程式代碼會將 新增 UINavigationController
為 RootViewController
:
public class AppDelegate : UIApplicationDelegate
{
// class-level declarations
public override UIWindow Window
{
get;
set;
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
// create a new window instance based on the screen size
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new UIViewController();
controller.View.BackgroundColor = UIColor.LightGray;
controller.Title = "My Controller";
var navController = new UINavigationController(controller);
Window.RootViewController = navController;
// make the window visible
Window.MakeKeyAndVisible();
return true;
}
}
這會產生內嵌在流覽控制器內的控制器,如下所示:
建立檢視控制器
既然我們已經瞭解如何將控制器新增為 RootViewController
視窗的 ,讓我們看看如何在程式碼中建立自定義檢視控制器。
新增名為 CustomViewController
的新類別,如下所示:
類別應該繼承自 UIViewController
,其位於 命名空間中 UIKit
,如下所示:
using System;
using UIKit;
namespace CodeOnlyDemo
{
class CustomViewController : UIViewController
{
}
}
初始化檢視
UIViewController
包含呼叫的方法,這個方法 ViewDidLoad
會在檢視控制器第一次載入記憶體時呼叫。 這是進行檢視初始化的適當位置,例如設定檢視的屬性。
例如,下列程式代碼會新增按鈕和事件處理程式,以在按下按鈕時,將新的檢視控制器推送至瀏覽堆疊:
using System;
using CoreGraphics;
using UIKit;
namespace CodyOnlyDemo
{
public class CustomViewController : UIViewController
{
public CustomViewController ()
{
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
View.BackgroundColor = UIColor.White;
Title = "My Custom View Controller";
var btn = UIButton.FromType (UIButtonType.System);
btn.Frame = new CGRect (20, 200, 280, 44);
btn.SetTitle ("Click Me", UIControlState.Normal);
var user = new UIViewController ();
user.View.BackgroundColor = UIColor.Magenta;
btn.TouchUpInside += (sender, e) => {
this.NavigationController.PushViewController (user, true);
};
View.AddSubview (btn);
}
}
}
若要在應用程式中載入此控制器,並示範簡單的導覽,請建立的新實例 CustomViewController
。 建立新的瀏覽控制器、傳入檢視控制器實例,並將新的瀏覽控制器設定為視窗的 AppDelegate
,RootViewController
如前所示:
var cvc = new CustomViewController ();
var navController = new UINavigationController (cvc);
Window.RootViewController = navController;
現在當應用程式載入時, CustomViewController
會在瀏覽控制器內載入 :
按下按鈕,會將 新的檢視控制器推送 至瀏覽堆疊:
建置檢視階層
在上述範例中,我們開始在程序代碼中建立使用者介面,方法是將按鈕新增至檢視控制器。
iOS 使用者介面是由檢視階層所組成。 其他檢視,例如卷標、按鈕、滑桿等,會新增為某些父檢視的子檢視。
例如,讓我們編輯 CustomViewController
來建立登入畫面,讓使用者可以輸入使用者名稱和密碼。 畫面將包含兩個文字欄位和一個按鈕。
新增文字欄位
首先,移除 [初始化檢視] 區段中新增的按鈕和事件處理程式。
藉由建立和初始化 UITextField
,然後將它新增至檢視階層,以新增用戶名稱的控件,如下所示:
class CustomViewController : UIViewController
{
UITextField usernameField;
public override void ViewDidLoad()
{
base.ViewDidLoad();
View.BackgroundColor = UIColor.Gray;
nfloat h = 31.0f;
nfloat w = View.Bounds.Width;
usernameField = new UITextField
{
Placeholder = "Enter your username",
BorderStyle = UITextBorderStyle.RoundedRect,
Frame = new CGRect(10, 82, w - 20, h)
};
View.AddSubview(usernameField);
}
}
當我們建立 UITextField
時,我們會設定 Frame
屬性來定義其位置和大小。 在 iOS 中,0,0 座標位於左上方,右側為 +x,而 +y 向下。 設定 Frame
和其他幾個屬性之後,我們會呼叫 View.AddSubview
將 新增 UITextField
至檢視階層。 這會使 usernameField
屬性所參考之 UIView
實例 View
的子檢視。 子檢視會以高於父檢視的迭置順序新增,因此會出現在畫面上父檢視前面。
包含 UITextField
的應用程式如下所示:
我們可以以類似的方式新增 UITextField
密碼的 ,只有這次我們將 屬性設定 SecureTextEntry
為 true,如下所示:
public class CustomViewController : UIViewController
{
UITextField usernameField, passwordField;
public override void ViewDidLoad()
{
// keep the code the username UITextField
passwordField = new UITextField
{
Placeholder = "Enter your password",
BorderStyle = UITextBorderStyle.RoundedRect,
Frame = new CGRect(10, 114, w - 20, h),
SecureTextEntry = true
};
View.AddSubview(usernameField);
View.AddSubview(passwordField);
}
}
設定 SecureTextEntry = true
會隱藏使用者輸入的 UITextField
文字,如下所示:
新增按鈕
接下來,我們將新增按鈕,讓使用者可以提交使用者名稱和密碼。 按鈕會像任何其他控件一樣新增至檢視階層,方法是再次將它當做自變數傳遞給父檢視的 AddSubview
方法。
下列程式代碼會新增 按鈕,並註冊 事件的事件處理程式 TouchUpInside
:
var submitButton = UIButton.FromType (UIButtonType.RoundedRect);
submitButton.Frame = new CGRect (10, 170, w - 20, 44);
submitButton.SetTitle ("Submit", UIControlState.Normal);
submitButton.TouchUpInside += (sender, e) => {
Console.WriteLine ("Submit button pressed");
};
View.AddSubview(submitButton);
現在,登入畫面隨即出現,如下所示:
不同於舊版 iOS,預設按鈕背景是透明的。 改變按鈕 BackgroundColor
的屬性會變更下列專案:
submitButton.BackgroundColor = UIColor.White;
這會導致方形按鈕,而不是一般的圓邊按鈕。 若要取得四捨五入邊緣,請使用下列代碼段:
submitButton.Layer.CornerRadius = 5f;
透過這些變更,檢視看起來會像這樣:
將多個檢視新增至檢視階層
iOS 提供使用 將多個檢視新增至檢視階層 AddSubviews
的設施。
View.AddSubviews(new UIView[] { usernameField, passwordField, submitButton });
新增按鈕功能
按兩下按鈕時,您的使用者會預期會發生一些情況。 例如,會顯示警示,或巡覽至另一個畫面。
讓我們新增一些程式代碼,將第二個檢視控制器推送至流覽堆疊。
首先,建立第二個檢視控制器:
var loginVC = new UIViewController () { Title = "Login Success!"};
loginVC.View.BackgroundColor = UIColor.Purple;
然後,將功能新增至 TouchUpInside
事件:
submitButton.TouchUpInside += (sender, e) => {
this.NavigationController.PushViewController (loginVC, true);
};
導覽如下所示:
請注意,根據預設,當您使用導航控制器時,iOS 會提供應用程式導覽列和返回按鈕,讓您能夠透過堆棧返回。
逐一查看檢視階層
可以逐一查看子檢視階層,並挑選任何特定檢視。 例如,若要尋找每個 UIButton
按鈕,並將該按鈕提供給不同的 BackgroundColor
,可以使用下列代碼段
foreach(var subview in View.Subviews)
{
if (subview is UIButton)
{
var btn = subview as UIButton;
btn.BackgroundColor = UIColor.Green;
}
}
不過,如果檢視所針對的檢視是 , UIView
則無法運作,因為所有檢視都會回復為 UIView
,因為加入父檢視的物件本身會繼承 UIView
。
處理旋轉
如果使用者將裝置旋轉為橫向,控件不會適當地重設大小,如下列螢幕快照所示:
修正此問題的其中一種方式是在每一個檢視上設定 AutoresizingMask
屬性。 在這裡情況下,我們想要讓控制項水平縮放,因此我們會設定每個 AutoresizingMask
。 下列範例適用於 usernameField
,但同樣需要套用至檢視階層中的每個小工具。
usernameField.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
現在,當我們旋轉裝置或模擬器時,所有專案會延展以填滿額外的空間,如下所示:
建立自定義檢視
除了使用屬於 UIKit 的控制件之外,也可以使用自訂檢視。 您可以藉由繼承 和 UIView
覆寫 Draw
來建立自定義檢視。 讓我們建立自定義檢視,並將其新增至檢視階層來示範。
繼承自UIView
我們需要做的第一件事是建立自定義檢視的類別。 我們將使用 Visual Studio 中的類別 範本來執行此動作,以新增名為的 CircleView
空類別。 基類應該設定為 UIView
,我們記得在 命名空間中 UIKit
。 我們也需要 System.Drawing
命名空間。 此範例中不會使用其他各種 System.*
命名空間,因此您可以隨意移除它們。
該類別看起來應該如下所示:
using System;
namespace CodeOnlyDemo
{
class CircleView : UIView
{
}
}
在 UIView 中繪製
每個 都有 UIView
一個 Draw
方法,在需要繪製時由系統呼叫。 Draw
不應該直接呼叫。 系統會在執行循環處理期間呼叫它。 第一次在檢視加入至檢視階層之後執行迴圈,就會呼叫其 Draw
方法。 當檢視標示為需要透過呼叫 或 SetNeedsDisplayInRect
檢視來繪製檢視時,會發生後續呼叫。Draw
SetNeedsDisplay
我們可以將繪圖程式代碼新增至檢視,方法是在覆寫 Draw
的方法內新增這類程序代碼,如下所示:
public override void Draw(CGRect rect)
{
base.Draw(rect);
//get graphics context
using (var g = UIGraphics.GetCurrentContext())
{
// set up drawing attributes
g.SetLineWidth(10.0f);
UIColor.Green.SetFill();
UIColor.Blue.SetStroke();
// create geometry
var path = new CGPath();
path.AddArc(Bounds.GetMidX(), Bounds.GetMidY(), 50f, 0, 2.0f * (float)Math.PI, true);
// add geometry to graphics context and draw
g.AddPath(path);
g.DrawPath(CGPathDrawingMode.FillStroke);
}
}
由於 CircleView
是 UIView
,我們也可以設定 UIView
屬性。 例如,我們可以在建構函式中設定 BackgroundColor
:
public CircleView()
{
BackgroundColor = UIColor.White;
}
若要使用剛才建立的 CircleView
,我們可以將其新增為現有控制器中的檢視階層的子檢視,就像我們在 和 之前一UILabels
UIButton
樣,也可以將它載入為新控制器的檢視。 讓我們執行後者。
載入檢視
UIViewController
具有名為 LoadView
的方法,由控制器呼叫以建立其檢視。 這是建立檢視的適當位置,並將它指派給控制器的 View
屬性。
首先,我們需要控制器,因此請建立名為 CircleController
的新空白類別。
在 CircleController
中新增下列程式代碼,將 設定 View
為 CircleView
(您不應該在覆寫中呼叫 base
實作):
using UIKit;
namespace CodeOnlyDemo
{
class CircleController : UIViewController
{
CircleView view;
public override void LoadView()
{
view = new CircleView();
View = view;
}
}
}
最後,我們需要在運行時間呈現控制器。 讓我們在稍早新增的 [提交] 按鈕上新增事件處理程式,如下所示:
submitButton.TouchUpInside += delegate
{
Console.WriteLine("Submit button clicked");
//circleController is declared as class variable
circleController = new CircleController();
PresentViewController(circleController, true, null);
};
現在,當我們執行應用程式並點選 [提交] 按鈕時,會顯示具有圓形的新檢視:
建立啟動畫面
啟動畫面會在您的應用程式啟動時顯示為向用戶顯示回應的方式時顯示。 由於啟動畫面會在應用程式載入時顯示,因此無法在程式碼中建立,因為應用程式仍在記憶體中載入。
當您在 Visual Studio 中建立 iOS 專案時,會以 .xib 檔案的形式提供啟動畫面,您可以在專案內的 Resources 資料夾中找到。
您可以按兩下它,並在 Xcode 介面產生器中開啟它,以編輯它。
Apple 建議針對以 iOS 8 或更新版本為目標的應用程式使用 .xib 或 Storyboard 檔案,當您在 Xcode 介面產生器中啟動任一個檔案時,您可以使用 [大小類別] 和 [自動版面配置] 來調整您的配置,使其看起來良好,並針對所有裝置大小正確顯示。 除了 .xib 或 Storyboard 之外,還可以使用靜態啟動映射,以允許對以舊版為目標的應用程式支援。
如需建立啟動畫面的詳細資訊,請參閱下列檔:
重要
從 iOS 9 開始,Apple 建議使用分鏡腳本作為建立啟動畫面的主要方法。
建立 iOS 8 前應用程式的啟動映像
如果您應用程式以 iOS 8 之前的版本為目標,除了 .xib 或 Storyboard 啟動畫面之外,還可以使用靜態影像。
您可以在 Info.plist 檔案中設定此靜態影像,或在您的應用程式中設定為資產目錄(for iOS 7)。 您必須為每個裝置大小(320x480、640x960、640x1136)提供個別的映像,您的應用程式可能執行。 如需啟動畫面大小的詳細資訊,請檢視 啟動畫面影像 指南。
重要
如果您的 app 沒有啟動畫面,您可能會注意到它無法完全符合畫面。 如果是這種情況,您應該至少包含名為 Info.plist 的 640x1136 映像 Default-568@2x.png
。
摘要
本文討論如何在Visual Studio中以程式設計方式開發iOS應用程式。 我們已探討如何從空白項目範本建置專案,討論如何建立根檢視控制器,並將根檢視控制器新增至視窗。 然後,我們示範如何使用UIKit中的控制器內建立檢視階層,以開發應用程式畫面。 接下來,我們檢查了如何讓檢視以不同的方向適當配置,並瞭解如何透過子類別 UIView
化建立自定義檢視,以及如何在控制器內載入檢視。 最後,我們已探索如何將啟動畫面新增至應用程式。