Привет, Хабр! Представляю вашему вниманию перевод статьи Creating UIViews Constraints Programmatically Using PureLayout автора Aly Yaka.
Сегодня я проведу вас через создание простого пользовательского интерфейса мобильного приложения кодом, без использования раскадровок или NIB'ов. Я не буду вдаваться в дискуссии о том, что лучше, потому что у всего есть свои плюсы и минусы, поэтому просто оставлю ссылку, которая углубится в это дело.
Во второй части этого руководства, мы создадим некоторые наиболее часто используемые элементы пользовательского интерфейса мобильного приложения кодом, включая панель навигации, представление таблицы и ячейки динамического размера.
Это руководство было написано с использованием Xcode 9 и Swift 4. Я также предполагаю, что вы знакомы с Xcode, Swift и CocoaPods.
Без дальнейшего промедления, давайте начнем создавать наш проект: простое приложение Contact Card. Цель данной статьи — научить вас, как создавать пользовательский интерфейс вашего приложения в коде, и поэтому он не будет содержать никакой логики в отношении функциональности приложения, если только это не нужно для целей данного руководства.
Начните с запуска Xcode -> «Создать новый проект Xcode». Выберите «Single View App» и нажмите «Next».
Назовите проект как хотите, я решил назвать его «ContactCard». Снимите все три опции ниже и выберите Swift в качестве языка программирования, затем нажмите «Далее».
Выберите место на вашем компьютере, чтобы сохранить проект. Снимите флажок «Create Git Repository on my Mac».
Так как мы не будем использовать Storyboards или NIB'ы в этом проекте, удалите «Main.storyboard», который можно найти в Project Navigator:
После этого нажмите на проект в навигаторе проектов и на вкладке «General» найдите раздел с информацией о развертывании и удалите все, что написано в «Main Interface». Это то, что сообщает Xcode, какой файл Storyboard загружать при запуске приложения, но поскольку мы просто удалили «Main.storyboard», Xcode не найдет этот файл, что приведет к падению приложения.
Если вы сейчас запустите приложение, появится черный экран, так как у приложения теперь нет источника пользовательского интерфейса, поэтому в следующей части мы создадим его. Откройте «AppDelegate.swift» и внутри
Этот код обеспечивает окно для взаимодействия пользователя с приложением, которое обычно можно найти в «ViewController.swift». Для быстрой проверки того, что все работает, перейдите к «ViewController.swift» и в методе
Теперь запустите приложение.
Для навигации между файлами в Xcode используйте горячие клавиши — «??O», а затем введите имя файла или даже фрагмент кода, который вы ищете, и на экране появится список файлов, из которых вы можете выбрать.
После запуска приложения это должно быть результатом на экране вашего симулятора:
Конечно, мы не будем использовать этот отвратительный синий, поэтому просто измените фон на белый, заменив
Для создания нашего пользовательского интерфейса мы будем использовать библиотеку, которая сделает нашу жизнь намного проще. Чтобы установить PureLayout, вы должны сначала открыть свой терминал и набрав cd, затем пробел, перетащите папку вашего проекта в терминал и нажмите «Enter». Теперь выполните следующие команды внутри терминала:
Это должен быть вывод вашего терминала после выполнения второй команды:
После этого закройте Xcode, откройте папку в Finder, и вы должны найти «<имя вашего проекта> .xcworkspace». Это то, что мы откроем для доступа к нашему приложению, если нам когда-нибудь понадобится использовать CocoaPods. Теперь найдите файл с именем «PodFile» и напишите следующую строку под фразой
Снова запустите
Теперь, когда все настроено, давайте начнем с реальной работы. Перейдите на «ViewController.swift» и возьмите чашку кофе, потому что вот как будет выглядеть конечный результат:
Вставьте строку
Что касается изображения, сохраните любое изображение на рабочем столе, которое вы будете использовать в качестве аватара, и перетащите его в XCode в папке <Имя вашего проекта>, которая в моем случае называется «ContactCard», и установите флажок «Copy items if needed».
После этого напишите имя этого файла вместе с его расширением в объявлении UIImage вместо «avatar.jpg».
Для тех из вас, кто не знает, lazy-переменные похожи на обычные переменные, за исключением того, что они не инициализируются (или не выделяется какое-либо пространство памяти) до тех пор, пока они не потребуются или не будут вызваны в первый раз. Это означает, что ленивые переменные не инициализируются при инициализации контроллера представления, а скорее ожидают более позднего момента, когда они действительно необходимы, что экономит вычислительную мощность и пространство памяти для других процессов. Это особенно полезно в случае инициализации компонентов пользовательского интерфейса.
Как вы можете видеть внутри инициализации, строка
Чтобы сделать это изображение круглым, мы устанавливаем его угловой радиус равным половине его ширины или высоты, которая составляет 64,0 точки. Кроме этого, устанавливаем для свойства
Затем мы переходим к созданию UIView, который будет служить верхней частью представления позади аватара, окрашенного в серый цвет. Объявим следующую ленивую переменную для этого представления:
Прежде чем идти дальше, давайте создадим функцию
А теперь добавьте следующую строку в
Чтобы просто получить представление о том, как далеко мы продвинулись, давайте установим ограничения для этих двух видов. Создайте другую функцию с именем
Теперь внутри
К сожалению, это не то, что хотелось бы получить. Как видите, наш
Этот метод перенесет аватар с самого дна стека до вершины, так что выбирайте тот метод, который вам больше нравится. Конечно, для удобства чтения лучше добавить подпредставления в том порядке, в котором они должны отображаться, если они пересекаются, при этом следует помнить, что первое добавленное subviews будет находиться в нижней части стека, и поэтому любые другие пересекающиеся вьюхи появятся поверх него.
И вот как это должно выглядеть на самом деле:
Далее мы создадим сегментированный элемент управления, который представляет собой серую полосу, содержащую три раздела. На самом деле сегментированный элемент управления создать просто. Сделайте следующее:
Я считаю, что все понятно, единственное отличие состоит в том, что после инициализации мы предоставляем ему массив строк, каждая строка представляет собой заголовок одного из наших желаемых разделов. Мы также устанавливаем
Теперь давайте продолжим и добавим его в качестве подпредставления, вставив следующую строку в конец функции
Мы говорим сегментированному элементу управления, что хотим прикрепить его к левой стороне его superview, однако мы хотим немного увеличить интервал, а не прикреплять его непосредственно к краю экрана. Если вы заметили, я использую так называемую сетку из восьми точек, где все расстояния и размеры кратны восьми. Я делаю то же самое с правой стороны сегментированного элемента управления. Что касается последнего ограничения, то он говорит прикрепить вершину к основанию аватара с интервалом в 16 точек.
После добавления указанных выше ограничений в
Теперь перейдем к последней части пользовательского интерфейса учебника, которая представляет собой кнопку «Редактировать». Добавьте следующую lazy-переменную:
Не беспокойтесь о том, насколько большая инициализация, но обратите внимание на то, как я устанавливаю заголовок и его цвет, вызывая функции
Теперь добавьте кнопку как subview, как и остальные компоненты, и добавьте следующие ограничения, чтобы она появилась там, где должна быть:
Здесь мы устанавливаем только правые и верхние ограничения для кнопки, т.к мы дали ей размер, она не будет расширяться и больше ничего не понадобится. Теперь запустите проект, чтобы увидеть конечный результат:
Попрактикуйтесь, добавьте столько элементов интерфейса, сколько хотите. Создайте представления любого приложения, которые вы считаете сложными. Начните с простого и постепенно наращивайте сложность. Попробуйте нарисовать компоненты пользовательского интерфейса на листе бумаги, чтобы представить, как они сочетаются друг с другом.
Во второй части я расширяю это руководство для создания панели навигации, табличного представления и ячеек динамического размера в коде.
Сегодня я проведу вас через создание простого пользовательского интерфейса мобильного приложения кодом, без использования раскадровок или NIB'ов. Я не буду вдаваться в дискуссии о том, что лучше, потому что у всего есть свои плюсы и минусы, поэтому просто оставлю ссылку, которая углубится в это дело.
Во второй части этого руководства, мы создадим некоторые наиболее часто используемые элементы пользовательского интерфейса мобильного приложения кодом, включая панель навигации, представление таблицы и ячейки динамического размера.
Обзор
Это руководство было написано с использованием Xcode 9 и Swift 4. Я также предполагаю, что вы знакомы с Xcode, Swift и CocoaPods.
Без дальнейшего промедления, давайте начнем создавать наш проект: простое приложение Contact Card. Цель данной статьи — научить вас, как создавать пользовательский интерфейс вашего приложения в коде, и поэтому он не будет содержать никакой логики в отношении функциональности приложения, если только это не нужно для целей данного руководства.
Создание constraints программно с PureLayout
Настройка проекта
Начните с запуска Xcode -> «Создать новый проект Xcode». Выберите «Single View App» и нажмите «Next».
Назовите проект как хотите, я решил назвать его «ContactCard». Снимите все три опции ниже и выберите Swift в качестве языка программирования, затем нажмите «Далее».
Выберите место на вашем компьютере, чтобы сохранить проект. Снимите флажок «Create Git Repository on my Mac».
Так как мы не будем использовать Storyboards или NIB'ы в этом проекте, удалите «Main.storyboard», который можно найти в Project Navigator:
После этого нажмите на проект в навигаторе проектов и на вкладке «General» найдите раздел с информацией о развертывании и удалите все, что написано в «Main Interface». Это то, что сообщает Xcode, какой файл Storyboard загружать при запуске приложения, но поскольку мы просто удалили «Main.storyboard», Xcode не найдет этот файл, что приведет к падению приложения.
Создание 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», а затем введите имя файла или даже фрагмент кода, который вы ищете, и на экране появится список файлов, из которых вы можете выбрать.
После запуска приложения это должно быть результатом на экране вашего симулятора:
Конечно, мы не будем использовать этот отвратительный синий, поэтому просто измените фон на белый, заменив
.blue
на .white
внутри viewDidLoad ()
.Разработка UI
Для создания нашего пользовательского интерфейса мы будем использовать библиотеку, которая сделает нашу жизнь намного проще. Чтобы установить PureLayout, вы должны сначала открыть свой терминал и набрав cd, затем пробел, перетащите папку вашего проекта в терминал и нажмите «Enter». Теперь выполните следующие команды внутри терминала:
- pod init
- pod install
Это должен быть вывод вашего терминала после выполнения второй команды:
После этого закройте Xcode, откройте папку в Finder, и вы должны найти «<имя вашего проекта> .xcworkspace». Это то, что мы откроем для доступа к нашему приложению, если нам когда-нибудь понадобится использовать CocoaPods. Теперь найдите файл с именем «PodFile» и напишите следующую строку под фразой
use_frameworks!
pod “PureLayout”
Снова запустите
pod install
в своем терминале, а затем соберите свой проект, нажав «Command + B».Перерыв на кофе
Теперь, когда все настроено, давайте начнем с реальной работы. Перейдите на «ViewController.swift» и возьмите чашку кофе, потому что вот как будет выглядеть конечный результат:
Создание 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».
После этого напишите имя этого файла вместе с его расширением в объявлении 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()
. Это должен быть окончательный вывод:Выводим аватар на передний план
К сожалению, это не то, что хотелось бы получить. Как видите, наш
upperView
лежит поверх аватара. Это связано с тем, что мы добавили аватар в качестве subviews
перед upperView
, и поскольку эти подпредставления расположены в некотором виде в стеке, получается такой результат. Чтобы исправить это, мы можем просто заменить эти две строки друг на друга, но есть еще одна хитрость, которую я хочу показать вам, а именно: self.view.bringSubview (toFront: avatar)
.Этот метод перенесет аватар с самого дна стека до вершины, так что выбирайте тот метод, который вам больше нравится. Конечно, для удобства чтения лучше добавить подпредставления в том порядке, в котором они должны отображаться, если они пересекаются, при этом следует помнить, что первое добавленное subviews будет находиться в нижней части стека, и поэтому любые другие пересекающиеся вьюхи появятся поверх него.
И вот как это должно выглядеть на самом деле:
Создание сегментированного контроля
Далее мы создадим сегментированный элемент управления, который представляет собой серую полосу, содержащую три раздела. На самом деле сегментированный элемент управления создать просто. Сделайте следующее:
lazy var segmentedControl: UISegmentedControl = {
let control = UISegmentedControl(items: ["Personal", "Social", "Resume"])
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()
запустите код и убедитесь, что он выглядит следующим образом:Добавление кнопки
Теперь перейдем к последней части пользовательского интерфейса учебника, которая представляет собой кнопку «Редактировать». Добавьте следующую 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)
Здесь мы устанавливаем только правые и верхние ограничения для кнопки, т.к мы дали ей размер, она не будет расширяться и больше ничего не понадобится. Теперь запустите проект, чтобы увидеть конечный результат:
Несколько последних заметок
Попрактикуйтесь, добавьте столько элементов интерфейса, сколько хотите. Создайте представления любого приложения, которые вы считаете сложными. Начните с простого и постепенно наращивайте сложность. Попробуйте нарисовать компоненты пользовательского интерфейса на листе бумаги, чтобы представить, как они сочетаются друг с другом.
Во второй части я расширяю это руководство для создания панели навигации, табличного представления и ячеек динамического размера в коде.