Создание пользовательских интерфейсов iOS в коде в Xamarin.iOS

Пользовательский интерфейс приложения iOS похож на магазин — приложение обычно получает одно окно, но оно может заполнить окно столько объектов, сколько требуется, и объекты и договоренности могут быть изменены в зависимости от того, что приложение хочет отобразить. В этом сценарии объекты — то, что видит пользователь — называются представлениями. Чтобы создать один экран в приложении, представления располагаются друг над другом в виде иерархии представлений содержимого, а управляет такой иерархией отдельный контроллер представления. Приложения с несколькими экранами используют несколько иерархий представлений содержимого, каждая из которых имеет свой контроллер представления. Приложение размещает представления в окне, чтобы создать другую иерархию представлений содержимого в зависимости от экрана, на котором находится пользователь.

На следующей схеме показаны связи между окном, представлениями, вложенными представлениями и контроллером представления, которые позволяют вывести пользовательский интерфейс на экран устройства:

This diagram illustrates the relationships between the Window, Views, Subviews, and View Controller

Эти иерархии представлений можно создавать с помощью построителя интерфейсов Xcode, однако хорошо иметь базовое представление о том, как работать полностью в коде. В этой статье рассматриваются некоторые основные моменты для создания и запуска с помощью разработки пользовательского интерфейса только для кода.

Создание проекта только для кода

Пустой шаблон проекта iOS

Сначала создайте проект iOS в Visual Studio с помощью > проекта File New Project > Visual C# > i Телефон & iPad iPad > iOS App (Xamarin), показанного ниже:

New Project Dialog

Затем выберите шаблон проекта пустого приложения :

Select a Template Dialog

Шаблон пустого проекта добавляет в проект 4 файла:

Project Files

  1. AppDelegate.cs — содержит UIApplicationDelegate подкласс, AppDelegate который используется для обработки событий приложения из iOS. Окно приложения создается в методеAppDelegateFinishedLaunching.
  2. Main.cs — содержит точку входа для приложения, которая задает класс для объекта AppDelegate .
  3. Info.plist — файл списка свойств, содержащий сведения о конфигурации приложения.
  4. Permissions.plist — файл списка свойств, содержащий сведения о возможностях и разрешениях приложения.

Приложения iOS создаются с помощью шаблона MVC. Первый экран, на котором отображается приложение, создается из корневого контроллера представления окна. Дополнительные сведения о шаблоне MVC см. в руководстве по iOS Multiscreen .

Реализация 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 из свойства. Приведенный выше код изменяет свойство UIColor.LightGray представления BackgroundColor таким образом, чтобы он был видимым, как показано ниже:

The View's background is a visible light gray

Мы можем задать любой UIViewController подкласс RootViewController таким образом, как и таким образом, включая контроллеры из UIKit, а также те, которые мы пишем сами. Например, следующий код добавляет в 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;
    }
}

Это создает контроллер, вложенный в контроллер навигации, как показано ниже:

The controller nested within the navigation controller

Создание контроллера представления

Теперь, когда мы узнали, как добавить контроллер в качестве 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 он загружается внутри контроллера навигации:

The CustomViewController is loaded inside a navigation controller

Нажатие кнопки приведет к отправке нового контроллера представления в стек навигации:

A new View Controller pushed onto the navigation stack

Создание иерархии представлений

В приведенном выше примере мы начали создавать пользовательский интерфейс в коде, добавив кнопку в контроллер представления.

Пользовательские интерфейсы 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 ссылается свойство. Вложенное представление добавляется с z-порядком, превышающим его родительское представление, поэтому оно отображается перед родительским представлением на экране.

Приложение с включенным UITextField приложением показано ниже:

The application with the UITextField included

Мы можем добавить 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 , как показано ниже:

Setting SecureTextEntry true hides the text entered by the user

Добавление кнопки

Затем мы добавим кнопку, чтобы пользователь смог отправить имя пользователя и пароль. Кнопка добавляется в иерархию представлений, как и любой другой элемент управления, передав его в качестве аргумента методу родительского представления 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);

На этом месте появится экран входа, как показано ниже:

The login screen

В отличие от предыдущих версий iOS, фон кнопки по умолчанию является прозрачным. Изменение свойства кнопки BackgroundColor изменяется следующим образом:

submitButton.BackgroundColor = UIColor.White;

Это приведет к квадратной кнопке, а не обычной закругляемой краю кнопки. Чтобы получить округленный край, используйте следующий фрагмент кода:

submitButton.Layer.CornerRadius = 5f;

При этих изменениях представление будет выглядеть следующим образом:

An example run of the view

Добавление нескольких представлений в иерархию представлений

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);
            };

Навигация показана ниже:

The navigation is illustrated in this chart

Обратите внимание, что по умолчанию при использовании контроллера навигации 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.

Обработка поворота

Если пользователь поворачивает устройство на альбомное, элементы управления не изменяются соответствующим образом, как показано на следующем снимках экрана:

If the user rotates the device to landscape, the controls do not resize appropriately

Один из способов исправить это — задать AutoresizingMask свойство для каждого представления. В этом случае мы хотим, чтобы элементы управления тянулись по горизонтали, поэтому мы задали каждый AutoresizingMaskэлемент управления. В следующем примере usernameFieldиспользуется, но то же самое потребуется применить к каждому гаджету в иерархии представлений.

usernameField.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;

Теперь при повороте устройства или симулятора все растягивается, чтобы заполнить дополнительное пространство, как показано ниже:

All the controls stretch to fill the additional space

Создание пользовательских представлений

Помимо использования элементов управления, которые являются частью UIKit, можно также использовать пользовательские представления. Настраиваемое представление можно создать, наследуя от UIView и переопределяя Draw. Создадим пользовательское представление и добавим его в иерархию представлений, чтобы продемонстрировать.

Наследование от UIView

Первое, что необходимо сделать, — создать класс для пользовательского представления. Мы сделаем это с помощью шаблона класса в Visual Studio, чтобы добавить пустой класс с именем CircleView. Базовый класс должен иметь значение UIView, которое мы вспоминаем в UIKit пространстве имен. Нам также потребуется System.Drawing пространство имен. Другие различные System.* пространства имен не будут использоваться в этом примере, поэтому вы можете удалить их.

Класс должен выглядеть следующим образом:

using System;

namespace CodeOnlyDemo
{
    class CircleView : UIView
    {
    }
}

Рисование в UIView

Каждый UIView имеет Draw метод, вызываемый системой, когда он должен быть нарисован. Draw никогда не следует вызывать напрямую. Он вызывается системой во время обработки цикла выполнения. При первом выполнении цикла выполнения после добавления представления в иерархию представлений вызывается его Draw метод. Последующие вызовы Draw возникают, когда представление помечается как необходимое для рисования путем вызова либо SetNeedsDisplaySetNeedsDisplayInRect в представлении.

Мы можем добавить код рисования в наше представление, добавив такой код в переопределенный 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 значение a 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);
};

Теперь при запуске приложения и нажатии кнопки отправки отображается новое представление с кругом:

The new view with a circle is displayed

Создание экрана запуска

Экран запуска отображается при запуске приложения в качестве способа отображения пользователям, которые они реагируют. Так как экран запуска отображается при загрузке приложения, его невозможно создать в коде, так как приложение по-прежнему загружается в память.

При создании проекта iOS в Visual Studio экран запуска предоставляется в виде XIB-файла, который можно найти в папке "Ресурсы " внутри проекта.

Это можно изменить, дважды щелкнув его и открыв его в конструкторе интерфейсов Xcode.

Apple рекомендует использовать XIB-файл или раскадровки для приложений, предназначенных для iOS 8 или более поздней версии, при запуске любого файла в Построителе интерфейсов Xcode можно использовать классы размера и автоматический макет для адаптации макета таким образом, чтобы он выглядел хорошо и отображался правильно для всех размеров устройств. Статический образ запуска можно использовать в дополнение к XIB или Storyboard, чтобы обеспечить поддержку приложений, предназначенных для более ранних версий.

Дополнительные сведения о создании экрана запуска см. в следующих документах:

Внимание

По состоянию на iOS 9 Apple рекомендует использовать раскадровки в качестве основного метода создания экрана запуска.

Создание образа запуска для приложений до iOS 8

Статический образ можно использовать в дополнение к экрану запуска XIB или Storyboard, если вы используете версии приложений, предшествующие iOS 8.

Этот статический образ можно задать в файле Info.plist или в качестве каталога активов (для iOS 7) в приложении. Вам потребуется предоставить отдельные изображения для каждого размера устройства (320x480, 640x960, 640x136), на которых может работать ваше приложение. Дополнительные сведения о размерах экрана запуска см. в руководстве по изображениям экрана запуска.

Внимание

Если у вашего приложения нет экрана запуска, вы можете заметить, что он не полностью соответствует экрану. Если это так, необходимо включить по крайней мере изображение 640x1136 с именем Default-568@2x.png info.plist.

Итоги

В этой статье описывается, как программно разрабатывать приложения iOS в Visual Studio. Мы рассмотрели, как создать проект из пустого шаблона проекта, обсудив создание и добавление корневого контроллера представления в окно. Затем мы показали, как использовать элементы управления из UIKit для создания иерархии представлений в контроллере для разработки экрана приложения. Далее мы рассмотрели, как сделать представления соответствующим образом выложены в разных ориентациях, и мы узнали, как создать пользовательское представление путем подкласса UIView, а также как загрузить представление в контроллере. Наконец, мы изучили, как добавить экран запуска в приложение.