Очнувшись первым и увидев Олю без сознания, Саша подорвался и, подбежав к ней, надел на нее кислородную маску. Потом немного придя в себя, понял, что сам он спокойно дышит и без маски. Осознав это, он закашлялся, показалось, что воздуха не хватает, и надо срочно искать маску и себе.
− Нормально, состав воздуха примерно как на Земле. Просто привыкнуть надо. В этом так называемом воздухе помимо кислородов и прочих азотов, примешан еще какой−то газ не из системы дядюшки Менделеева. Всего пару тысячных процента, но дышать немного непривычно. – сказал угрюмый Коля, сидящий поодаль.
− Зачем ты это сделал? Зачем ты закрыл дверь? Слава…
− Он был в ярости, а у меня, знаешь ли нет пояса по карате, даже белого.
− Это ты сделал дыру в корабле? − Саша аккуратно положил голову Оли на землю, вскочил и побежал к Коле, замахнулся и ударил его в лицо со всей силы, они оба упали на землю. − Я тебя сейчас убью, тварь, это из−за тебя мы непонятно где, из−за тебя Слава погиб, это ты все время хотел назад, а назад не вернешься, ты блин член команды или слабак?
− Хочешь убить меня? Ну давай, все равно мы здесь умрем. Мы непонятно где, здесь ничего нет, и улететь отсюда мы не сможем, давай, убей меня. Слабо?
− Прекратите, − Аня подбежала к Саше и начала его оттаскивать, только что очнувшаяся Оля вскочила и хотела помочь подруге, как вдруг упала и закричала так пронзительно, что все сразу остановились, Саша вжал в землю Колю и побежал к Оле.
Чтобы создать что-то исключительное, нужно проявить внимание ко всем деталям.
Что кроется за этими двумя странными буквами? Interface Builder
. (На русском обычно используется вариант Интерфейс Билдер без перевода) Пожалуй, лучшая программа для создания интерфейсов приложений. Если Вы работали с Visual Studio
или Android Studio
, то такой подход покажется в миллион раз проще - дизайн отделён от кода. Это даст возможность выстроить очень классную схему работы:
Чтобы открыть IB, просто выберите файл типа .xib
или .storyboard
:
XIB
- содержит вид (представление или view) для одного отдельного объектаStoryBoard
- содержит множество разных представлений, связанных в сцены и переходы между нимиПерейдите к файлу main.storyboard
:
Здесь представлена иерархия сцен и объектов в сценах:
Вы можете скрыть левую панель (1) (она ещё называется Document Outline
), нажав по кнопке 5. Нажав ещё раз, Вы сможете вернуть её.
Обратите внимание на кнопки в области 6 (слева направо):
Эта часть называется библиотекой объектов и состоит из 4 вкладок (слева направо):
Включите третью вкладку.
Найдите в колекции объектов IB кнопку - Button
. Вы можете пролистать его в виде списка или коллекции или же просто ввести слово в строку поиска.
Захватите кнопку и перетащите на полотно контроллера - его вид выделится синим, а при приближении к верхнему углу появятся линейки. Расположите кнопку на пересечении двух линеек. Это верхний левый угол безопасной зоны - выше него лежит статус-бар, а левее располагать опасно для касаний пользователя.
Теперь обратим внимание на верхнюю часть правой панели (слева направо):
keyPath
и тому подобным. На данный момент в нём указан класс объекта UIButton
- это стандартный класс кнопки в UIKit
.3-6-ые инспекторы выступают контекстными - их содержимое и сам факт наличия меняются в зависимости от объекта, на котором они открыты.
Вы думали, что здесь будет простая геометрия? Сильно-сильно ошибались. По историческим и техническим причинам начало координат (0; 0)
находится в левом верхнем углу экрана, ось x
направлена как обычно вправо, а вот ось y
направлена вниз из центра координат.
Вам нужен способ связать интерфейс с кодом. Для связи объектов с кодом используются аутлеты (или выходы, или outlets
). Для выполнения каких-то действий и интерактивности используются действия (actions
)
Давайте откроем на экране сразу два окна - StoryBoard
и корреспондирующий со сценой контроллер представления. Для этого нажмите по редактору-помощнику наверху экрана (2). Чтобы затем вернутся в единичный вид нажмите по (1) (обычный редактор).
Теперь создадим аутлет - для этого зажмите клавишу ctrl, захватите кнопку и перетащите стрелку от нее в тот участок кода, где хотите её вставить.
Введите в поле Name
имя объекта: давайте назовём его myFirstButton
. Теперь нажмите на кнопке connect
. Получится:
@IBOutlet weak var myFirstButton: UIButton!
Что за страхолюдина у нас появилась? И так много слов. Сперва обратим внимание на круг напротив декларации кнопки - он отражает связь кнопки с объектов раскадровки.
@IBOutlet
- помните, как мы рассматривали атрибуты наподобие @escaping
? Это тоже одно из них. @IBOutlet
ставится перед объектами в коде, которые Вы хотите открыть для интерфейс билдера.
weak
- указание на то, что аутлет связан с контролером слабым образом. Припоминаете, как мы разбивали циклы сильных ссылок? Здесь это ключевое слово используется по той же причине - с одной стороны на кнопку указывает раскадровка, с другой стороны контроллер вида. Слабость данной ссылки позволяет ей спокойно отправиться через реку Стикс при выхода из памяти контроллера или раскадровки.
Так как это слабая ссылка и неявно извлекаемый опционал, то объект объявлен как переменная.
UIButton!
- кнопка объявлена как неявно извлекаемый опционал, так как это слабая ссылка.
Добавьте в метод viewDidLoad()
следующий код:
self.myFirstButton.setTitleColor(.red, for: .normal)
Почему мы ссылаемся на self
? Как сказано в предыдущем курсе - привычка, позволяющая отделить объекты внешней природы и внутренние объекты контроллера.
Затем мы вызываем метод:
setTitleColor(_ color: UIColor?, for state: UIControlState)
Первым аргументом принимается объект типа UIColor?
, класса который отображает цвета для интерфейса - таким цветом будет отображаться при состоянии state
. Состояние отображается через тип UIControlState
.
И то, и другое можно использовать в качестве перечисления - мы просто можем указать цвет .red
, а состояние .normal
, что есть обычное состояние кнопки. Попробуйте самостоятельно повыбирать другие цвета.
Заметьте, что цвет кнопки не изменился. Это связано с тем, что метод viewDidLoad()
будет вызван только при запуске интерфейса в собранном приложении.
Обратите внимание, что если выбрать кнопку и зайти в раздел её свойств, то мы увидим свойство Text Color
:
Оно также даёт возможность задать цвет текста кнопки в нормальном состоянии.
Что же будет иметь приоритет? Метод. Ибо он будет вызван после установки свойств раскадровки.
А что же лучше всего использовать? Свойства. Если Вы можете задать что-то через раскадровку, то делайте это именно так. Мы узнаем в будущем, что не все свойства можно задать через IB, однако он для того и предназначен - разгрузить код от рутинной работы.
Запустите приложение и Вы увидите: цвет кнопки стал красным. Однако нажатие на кнопку ничего не даёт. Нам ещё придётся задать ей действия.
А пока прервёмся на время и поиграемся с красками.
Выберите цвет текста и нажмите по разделу other
(другой цвет).
Вы можете выбрать цвет через краски на палитре. opacity
задаёт прозрачность цвета. Вы также можете воспользоваться пипеткой.
Вы можете задать цвет через пространства RGB или CMYK, причём задание цвета возможно и через шкалу 0-255 и через дробные числа от 0 до 1.
Вы можете выбрать цвет из наборов готовых цветов.
Возможно выбрать цвет через спектр.
А также через наборы профессиональных классических карандашей.
Теперь вернемся в наш метод viewDidLoad()
и попробуем добавить следующий код:
self.myFirstButton.titleLabel?.textColor = #colorLiteral(red: 0.9466800094, green: 0.3168402612, blue: 0.2236361504, alpha: 1)
Теперь после знака присвоения начните вводить Color literal
и выберите в списке предложений Xcode цветовой литерал. Теперь Вы можете очень легко выбрать цвет, как делали это в инспекторе атрибутов.
Что же мы сделали здесь?
Мы попытались присвоить цвет свойству textColor
свойства titleLabel
у нашей кнопки. И ничего не вышло. Свойство titleLabel
имеет тип UILabel?
, где UILabel
- тип надписи, а опциональным оно является, так как кнопка может и не иметь текста (например, когда она представлена рисунком).
Запустите приложение. Ничего не изменилось. Именно поэтому нужно использовать специальный метод. Сотрите это нововведение.
Давайте наполним жизнь нашей кнопки смыслом:
Как и раньше захватите кнопку и с зажатым ctrl перетащите её в правое окно и отпустите. В уже знакомом окне выберите не Outlet
, а Action
. Введите название buttonPressed
. В поле Type
Вы можете выбрать тип объекта, который посылает действие - по-умолчанию, это Any
, но так как мы будем использовать его только для кнопки, то давайте зададим тип UIButton
.
У нас появилось три новых строчки кода:
@IBAction func buttonPressed(_ sender: UIButton) {
}
@IBAction
- такой же атрибут, как и в случае с аутлетом - он проецирует метод контроллера в IB, чтобы его можно было прикреплять к объектам.
Свойство sender
позволяет обрабатывать объект, отправивший данное сообщение.
Пропишем в этом методе следующее:
self.myFirstButton.setTitle("Кнопка", for: .normal)
Мы использовали ещё один новый для нас метод:
setTitle(_ title: String?, for state: UIControlState)
Тут title
- значение нового заголовка кнопки, а state
- состояние. Это свойство можно также изменить через инспектор атрибутов в свойстве Title
.
Запустим приложение и увидим, что при нажатии на кнопку, её текст меняется. Вот же магия вне Хогвартса.
Заметьте, что текст в кнопку не вместился. В будущем мы узнаем, что произошло и как с таким бороться.
Теперь посмотрите на инспектор связей, соединений у кнопки. Событие Touch Up Inside
связано теперь с действием buttonPressed:
у ViewController
. Это событие возникает при нажатии на кнопку и отпускании её. Этот подход продиктован принципами дизайна и пользовательского опыта: если пользователь нажал на кнопку, но ещё не отпустил её, то у него есть возможность передумать - он может отвести от неё палец и отпустить, а действия не будет.
Объекты создаются и программно (хотя напомним, лучше делайте все свои грязные дизайнерские дела через IB
).
Добавим следующий код в метод viewDidLoad()
:
let label = UILabel(frame: CGRect(x: 100, y: 100, width: 222, height: 213))
Данный код создаёт лейбл через конструктор UILabel(frame:)
.
В качестве аргумента конструктора выступает некий frame
(кадр) - тут под ним понимается геометрическая фигура.
Для создания прямоугольника используется CGRect(x:y:width:height:)
, объект фреймворка Core Graphics
(CG
). Но постойте же? Мы не импортировали его!
Фактически CG
, как и Foundation
, входит в состав UIKit
.
Этот конструктор для прямоугольника принимает значения позиции по x
и y
, а также ширину и высоту. Причем, как Вы могли заметить, доступны варианты с инициализацией через Int
, Double
и нечто новое - CGFloat
. Это специальный тип для чисел с плавающей запятой, используемый в Core Graphics. И хотя он называется Float, он будет иметь точность, равную максимально возможному Double на данной платформе. Так как, начиная с ::iOS11:: на ::iOS:: больше нет 32-битных приложений и процессоров, то CGFloat
почти эквивалентен Double
. Заметим, что и CGRect
, и CGFloat
являются структурами, а не классами.
Несмотря на проделанную нами работу, никакой прямоугольник не появится. Добавим ещё пару штрихов в наш метод:
label.backgroundColor = #colorLiteral(red: 0.9466800094, green: 0.3168402612, blue: 0.2236361504, alpha: 1)
Данная инструкция задаст лейблу наш любимый цвет.
Далее зададим текст метки:
label.text = "Грут"
И этого всё равно мало - просто так созданный объект не появляется по волшебному щелчку.
Необходимо добавить его в представление.
У каждого UIViewController
есть свойство view
, возвращающее объект типа UIView
(базовый класс для всех представлений), у которого в свою очередь есть метод addSubview(_:)
. Воспользуемся им для добавления представления на форму:
self.view.addSubview(label)
Заметьте, что метод принимает базовый класс, а не метку, что значит, что можно добавить подвид в представление любого класса-потомка. И в любое представление можно добавить другое представление.