Pull to refresh

Создаем элементы интерфейса программно с помощью PureLayout (Часть 1)

Reading time8 min
Views15K
Привет, Хабр! Представляю вашему вниманию перевод статьи Creating UIViews Constraints Programmatically Using PureLayout автора Aly Yaka.

image

Сегодня я проведу вас через создание простого пользовательского интерфейса мобильного приложения кодом, без использования раскадровок или NIB'ов. Я не буду вдаваться в дискуссии о том, что лучше, потому что у всего есть свои плюсы и минусы, поэтому просто оставлю ссылку, которая углубится в это дело.

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

Обзор


Это руководство было написано с использованием Xcode 9 и Swift 4. Я также предполагаю, что вы знакомы с Xcode, Swift и CocoaPods.

Без дальнейшего промедления, давайте начнем создавать наш проект: простое приложение Contact Card. Цель данной статьи — научить вас, как создавать пользовательский интерфейс вашего приложения в коде, и поэтому он не будет содержать никакой логики в отношении функциональности приложения, если только это не нужно для целей данного руководства.

Создание constraints программно с PureLayout


Настройка проекта


Начните с запуска Xcode -> «Создать новый проект Xcode». Выберите «Single View App» и нажмите «Next».

image

Назовите проект как хотите, я решил назвать его «ContactCard». Снимите все три опции ниже и выберите Swift в качестве языка программирования, затем нажмите «Далее».

image

Выберите место на вашем компьютере, чтобы сохранить проект. Снимите флажок «Create Git Repository on my Mac».

Так как мы не будем использовать Storyboards или NIB'ы в этом проекте, удалите «Main.storyboard», который можно найти в Project Navigator:

image

После этого нажмите на проект в навигаторе проектов и на вкладке «General» найдите раздел с информацией о развертывании и удалите все, что написано в «Main Interface». Это то, что сообщает Xcode, какой файл Storyboard загружать при запуске приложения, но поскольку мы просто удалили «Main.storyboard», Xcode не найдет этот файл, что приведет к падению приложения.

image

Создание ViewController


Если вы сейчас запустите приложение, появится черный экран, так как у приложения теперь нет источника пользовательского интерфейса, поэтому в следующей части мы создадим его. Откройте «AppDelegate.swift» и внутри application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?), вставьте данный фрагмент кода:

self.window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
self.window?.rootViewController = viewController
self.window?.makeKeyAndVisible()

Этот код обеспечивает окно для взаимодействия пользователя с приложением, которое обычно можно найти в «ViewController.swift». Для быстрой проверки того, что все работает, перейдите к «ViewController.swift» и в методе viewDidLoad() вставьте следующую строку:

self.view.backgroundColor = .blue

Теперь запустите приложение.

Для навигации между файлами в Xcode используйте горячие клавиши — «⇧⌘O», а затем введите имя файла или даже фрагмент кода, который вы ищете, и на экране появится список файлов, из которых вы можете выбрать.

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

image

Конечно, мы не будем использовать этот отвратительный синий, поэтому просто измените фон на белый, заменив .blue на .white внутри viewDidLoad ().

Разработка UI


Для создания нашего пользовательского интерфейса мы будем использовать библиотеку, которая сделает нашу жизнь намного проще. Чтобы установить PureLayout, вы должны сначала открыть свой терминал и набрав cd, затем пробел, перетащите папку вашего проекта в терминал и нажмите «Enter». Теперь выполните следующие команды внутри терминала:

  • pod init
  • pod install

Это должен быть вывод вашего терминала после выполнения второй команды:

image

После этого закройте Xcode, откройте папку в Finder, и вы должны найти «<имя вашего проекта> .xcworkspace». Это то, что мы откроем для доступа к нашему приложению, если нам когда-нибудь понадобится использовать CocoaPods. Теперь найдите файл с именем «PodFile» и напишите следующую строку под фразой use_frameworks!

pod “PureLayout”

Снова запустите pod install в своем терминале, а затем соберите свой проект, нажав «Command + B».

Перерыв на кофе


Теперь, когда все настроено, давайте начнем с реальной работы. Перейдите на «ViewController.swift» и возьмите чашку кофе, потому что вот как будет выглядеть конечный результат:

image

Создание ImageView


Вставьте строку import PureLayout под import UIKit, чтобы у вас появилась возможность использовать библиотеку в этом файле. Затем, под объявлением класса и вне любой функции, мы начнем с создания lazy (ленивой) переменной Avatar ImageView следующим образом:

lazy var avatar: UIImageView = {
    let imageView = UIImageView(image: UIImage(named: "avatar.jpg"))
    imageView.autoSetDimensions(to: CGSize(width: 128.0, height: 128.0))
    imageView.layer.borderWidth = 3.0
    imageView.layer.borderColor = UIColor.lightGray.cgColor
    imageView.layer.cornerRadius = 64.0
    imageView.clipsToBounds = true
    return imageView
}()

Что касается изображения, сохраните любое изображение на рабочем столе, которое вы будете использовать в качестве аватара, и перетащите его в XCode в папке <Имя вашего проекта>, которая в моем случае называется «ContactCard», и установите флажок «Copy items if needed».

image

После этого напишите имя этого файла вместе с его расширением в объявлении UIImage вместо «avatar.jpg».

Для тех из вас, кто не знает, lazy-переменные похожи на обычные переменные, за исключением того, что они не инициализируются (или не выделяется какое-либо пространство памяти) до тех пор, пока они не потребуются или не будут вызваны в первый раз. Это означает, что ленивые переменные не инициализируются при инициализации контроллера представления, а скорее ожидают более позднего момента, когда они действительно необходимы, что экономит вычислительную мощность и пространство памяти для других процессов. Это особенно полезно в случае инициализации компонентов пользовательского интерфейса.

PureLayout в действии


Как вы можете видеть внутри инициализации, строка imageView.autoSetDimensions (to: CGSize (width: 128.0, height: 128.0)) является PureLayout в действии. Одной строкой мы устанавливаем ограничение как для высоты, так и для ширины UIImageView, и все необходимые NSLayoutConstraints создаются без необходимости огромных вызовов функций. Если вы имели дело с созданием ограничений программным способом, то вы, скорее всего, уже влюбились в эту замечательную библиотеку.

Чтобы сделать это изображение круглым, мы устанавливаем его угловой радиус равным половине его ширины или высоты, которая составляет 64,0 точки. Кроме этого, устанавливаем для свойства clipsToBounds значение true, которое сообщает изображению, что оно должно обрезать все, что находится за пределами радиуса, который мы только что установили.

Затем мы переходим к созданию UIView, который будет служить верхней частью представления позади аватара, окрашенного в серый цвет. Объявим следующую ленивую переменную для этого представления:

lazy var upperView: UIView = {
    let view = UIView()
    view.autoSetDimension(.height, toSize: 128)
    view.backgroundColor = .gray
    return view
}()

Добавление subviews


Прежде чем идти дальше, давайте создадим функцию func addSubviews (), которая добавляет только что созданные нами представления (и все другие, которые мы собираемся создать) в качестве subviews к контроллеру представления:

func addSubviews() {
    self.view.addSubview(avatar)
    self.view.addSubview(upperView)
}

А теперь добавьте следующую строку в viewDidLoad (): self.addSubviews ()

Настройка ограничений


Чтобы просто получить представление о том, как далеко мы продвинулись, давайте установим ограничения для этих двух видов. Создайте другую функцию с именем func setupConstraints() и вставьте следующие ограничения:

func setupConstraints() {
    avatar.autoAlignAxis(toSuperviewAxis: .vertical)
    avatar.autoPinEdge(toSuperviewEdge: .top, withInset: 64.0)
    upperView.autoPinEdge(toSuperviewEdge: .left)
    upperView.autoPinEdge(toSuperviewEdge: .right)
    upperView.autoPinEdgesToSuperviewEdges(with: .zero, excludingEdge: .bottom)
}

Теперь внутри viewDidLoad() вызовите setupConstraints(), следующим образом: self.setupConstraints(). Добавьте это ПОСЛЕ вызова addSubviews(). Это должен быть окончательный вывод:

image

Выводим аватар на передний план


К сожалению, это не то, что хотелось бы получить. Как видите, наш upperView лежит поверх аватара. Это связано с тем, что мы добавили аватар в качестве subviews перед upperView, и поскольку эти подпредставления расположены в некотором виде в стеке, получается такой результат. Чтобы исправить это, мы можем просто заменить эти две строки друг на друга, но есть еще одна хитрость, которую я хочу показать вам, а именно: self.view.bringSubview (toFront: avatar).

Этот метод перенесет аватар с самого дна стека до вершины, так что выбирайте тот метод, который вам больше нравится. Конечно, для удобства чтения лучше добавить подпредставления в том порядке, в котором они должны отображаться, если они пересекаются, при этом следует помнить, что первое добавленное subviews будет находиться в нижней части стека, и поэтому любые другие пересекающиеся вьюхи появятся поверх него.
И вот как это должно выглядеть на самом деле:

image

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


Далее мы создадим сегментированный элемент управления, который представляет собой серую полосу, содержащую три раздела. На самом деле сегментированный элемент управления создать просто. Сделайте следующее:

lazy var segmentedControl: UISegmentedControl = {
    let control = UISegmentedControl(items: ["Personal", "Social", "Resumè"])
    control.autoSetDimension(.height, toSize: 32.0)
    control.selectedSegmentIndex = 0
    control.layer.borderColor = UIColor.gray.cgColor
    control.tintColor = .gray
    return control
}()

Я считаю, что все понятно, единственное отличие состоит в том, что после инициализации мы предоставляем ему массив строк, каждая строка представляет собой заголовок одного из наших желаемых разделов. Мы также устанавливаем selectedSegmentIndex в 0, что говорит сегментированному элементу управления выделять/выбирать первый сегмент при инициализации. Остальное — просто стиль, с которым можно поиграться.

Теперь давайте продолжим и добавим его в качестве подпредставления, вставив следующую строку в конец функции addCubviews(): self.view.addSubview(segmentedControl) и его ограничения будут такими:

    segmentedControl.autoPinEdge(toSuperviewEdge: .left, withInset: 8.0)
    segmentedControl.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0)
    segmentedControl.autoPinEdge(.top, to: .bottom, of: avatar, withOffset: 16.0)

Мы говорим сегментированному элементу управления, что хотим прикрепить его к левой стороне его superview, однако мы хотим немного увеличить интервал, а не прикреплять его непосредственно к краю экрана. Если вы заметили, я использую так называемую сетку из восьми точек, где все расстояния и размеры кратны восьми. Я делаю то же самое с правой стороны сегментированного элемента управления. Что касается последнего ограничения, то он говорит прикрепить вершину к основанию аватара с интервалом в 16 точек.

После добавления указанных выше ограничений в func setupConstraints() запустите код и убедитесь, что он выглядит следующим образом:

image

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


Теперь перейдем к последней части пользовательского интерфейса учебника, которая представляет собой кнопку «Редактировать». Добавьте следующую lazy-переменную:

lazy var editButton: UIButton = {
    let button = UIButton()
    button.setTitle("Edit", for: .normal)
    button.setTitleColor(.gray, for: .normal)
    button.layer.cornerRadius = 4.0
    button.layer.borderColor = UIColor.gray.cgColor
    button.layer.borderWidth = 1.0
    button.tintColor = .gray
    button.backgroundColor = .clear
    button.autoSetDimension(.width, toSize: 96.0)
    button.autoSetDimension(.height, toSize: 32.0)
    return button
}()

Не беспокойтесь о том, насколько большая инициализация, но обратите внимание на то, как я устанавливаю заголовок и его цвет, вызывая функции button.setTitle и button.setTitleColor. По определенным причинам мы не можем установить заголовок кнопки, напрямую обращаясь к ее titleLabel, и это потому что для кнопки существуют разные состояния, и многим было бы удобно иметь разные заголовки/цвета для разных состояний.

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

editButton.autoPinEdge(.top, to: .bottom, of: upperView, withOffset: 16.0)
editButton.autoPinEdge(toSuperviewEdge: .right, withInset: 8.0)

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

image

Несколько последних заметок


Попрактикуйтесь, добавьте столько элементов интерфейса, сколько хотите. Создайте представления любого приложения, которые вы считаете сложными. Начните с простого и постепенно наращивайте сложность. Попробуйте нарисовать компоненты пользовательского интерфейса на листе бумаги, чтобы представить, как они сочетаются друг с другом.
Во второй части я расширяю это руководство для создания панели навигации, табличного представления и ячеек динамического размера в коде.
Tags:
Hubs:
Total votes 13: ↑12 and ↓1+11
Comments0

Articles