Результати пошуку
ITVDN: курси програмування
Відеокурси з
програмування
Підписка

300+ курсів за популярними IT-напрямками

Вибери свою IT спеціальність

Підписка
Підписка

300+ курсів за популярними IT-напрямками

Результати пошуку за запитом: обучение c*
Введення в розробку програм під iOS. Частина 2.

Автор: Volodymyr Bozhek

                                                       Здравствуйте дорогие читатели. В этом уроке мы добавим форму списка товаров на складе. Разберем класс “UITableViewController” и как с ним работать. Откройте проект Warehouse из предыдущего урока. Необходимо добавить в наш проект две View. На одной будут содержаться все товары, которые есть на складе. На второй форма редактирования товара. Для реализации View со списком всех товаров мы будем использовать готовый табличный класс контроллера “UITableViewController”. Данный класс уже реализовал все необходимые протоколы внутри себя для уверенной работы табличного контроллера. Есть и другой вариант, который мы не будем рассматривать в этом уроке, это сделать свой класс “UITableViewController”, унаследовшись от базового класса “UIViewController” и реализовав протоколы “UITableViewDelegate” и “UITableViewDatasource”. В панели навигатора откройте модуль “Main.storyboard”. Найдите в библиотеке компонентов, компонент “Table View Controller” и перетяните в пустую область дизайнера рядом с View входа в систему. В панели навигатора, нажмите правой кнопкой мыши на папке “Warehouse” и в контекстном меню выберите пункт “New File...”. Откроется диалоговое окно добавления новых модулей в проект. Найдите в нем модуль “Swift File”, выделите его и нажмите кнопку “Next”. Появится диалоговое окно задания названия добавляемого модуля. В поле “Save As” введите “SuppliesViewController.swift”, нажмите кнопку “Create”.                                                                                                           Откройте модуль “SuppliesViewController.swift”, вы увидите в нем только “import Foundation”, это добавление пространства имен на базовую библиотеку классов Swift (аналог в C#, пространство имен “System”). Удалите эту строку. Добавьте в этот модуль следующий код: Разберем это код построчно. На 9 строке, мы подключили пространство имен “UIKit” в нем содержатся классы для взаимодействия с компонентами пользовательского интерфейса в iOS. На 11 строке, мы объявили класс с именем “SuppliesViewController”, который наследуется от класса “UITableViewController”. На 12 строке, мы объявили строковый массив с именем “supplies”, в этом массиве содержится статический список товаров (чуть позже мы заменим его на модель). На 17 строке, мы переопределили метод “viewDidLoad” класса “UITableViewController”, данный метод вызывается когда View начинает загрузку. В этом методе необходимо делать предварительную инициализацию компонентов, какими либо данными. На 18 строке, мы вызываем базовый метод “viewDidLoad” класса “UITableViewController”, с помощью ключего слова “super” (аналог в C#, ключевое слово “base”). Начиная с 21 строки и по 33 строку, мы переопределяем поведение базового контроллера “UITableViewController”, для отображения данных в нем. Более конкретно, мы переопределяем реализацию протокола “UITableViewDataSource”. На 21 строке переопределенный метод “numberOfSections”, возвращает количество секций в нашем табличном представлении. Секции это разделители между сгруппированными наборами ячеек в табличном представлении. У нас будет одна секция. На 25 строке переопределенный метод “numberOfRowsInSection”, возвращает количество ячеек в секции. Секция у нас одна, поэтому возвращаем количество товаров, которые будут в этой секции. На 29 строке переопределенный метод “cellForRowAt indexPath”, возвращает представление ячейки таблицы. На 30 строке мы создаем экземпляр типа “UITableViewCell” с повторно используемым идентификатором “сell”, первый параметр в инициализаторе это стиль ячейки, второй параметр это идентификатор этой ячейки в табличном представлении. Данный метод вызывается столько раз, сколько ячеек мы добавляем в нашу таблицу. Каждый вызов данного метода содержит в аргументе “indexPath” текущую позицию ячейки относительно таблицы. На 31 строке мы обращаемся к компоненту “Label”, расположенному внутри ячейки, чтобы задать текст, для этого используем свойство “textLabel”, затем от этого свойства уже обращаемся к свойству “text”, чтобы задать текст компонента “Label”. Текст мы извлекаем из массива товаров “supplies” через subscript (по индексу). Чтобы получить текущий номер строки, необходимо обратиться к аргументу “indexPath” и обратиться к свойству “row”. На 32 строке мы возвращаем созданную ячейку. Эта ячейка затем внутренними механизмами будет добавлена в табличное представление. Откройте модуль “Main.storyboard”, выделите добавленный ранее “Table View Controller”. В панели свойств, откройте вкладку “Show the identity inspector”, найдите поле “Class”, введите значение “SuppliesViewController”. Через это свойство мы подвязываем класс контроллера “SuppliesViewController” к нашему табличному представлению в дизайнере. Найдите свойство “Storyboard ID”, задайте значение в нем “SuppliesViewController”. Данное свойство содержит уникальный идентификатор представления, этот идентификатор будет позднее использоваться нами при поиске табличного представления программно в экземпляре “Storyboard”. Теперь нам необходимо добавить в обработчик события на нажатие кнопки “Вход”, переход на табличное представление. Откройте модуль “ViewController”. Добавьте код в метод “loginButtonTapped”, как показано ниже (смотреть выделенный код): Разберем добавленный код: На 23 строке, правым операндом мы обращаемся к свойству “storyboard” контроллера “ViewController”, в этом свойстве содержится экземпляр модуля “Main.storyboard”. Вызов метода “instantiateViewController” производит поиск представления по идентификатору “Storyboard ID”, которое мы задавали ранее, в модуле “Main.Storyboard” и возвращает базовый класс контроллера “UIViewController”. Класс “UITableViewController” наследуется от класса “UIViewController”, соответственно допустимо приведение данного экземпляра к классу “UITableViewController”. Класс “SuppliesViewController“ наследуется от класса “UITableViewController”. Соответственно мы приводим полученный экземпляр в правом операнде к типу “SuppliesViewController” и присваиваем его левому операнду c именем “controller”. Левый операнд объявлен константой, поскольку он не будет в дальнейшем проходить переинициализацию. На 24 строке, мы обращаемся к свойству “navigationController”, в данном свойстве содержится экземпляр класса “UINavigationController” (сейчас он не инициализирован и содержит значение “nil”). Затем от экземпляра этого свойства мы вызываем метод “pushViewController”. Данный метод принимает два аргумента, экземпляр контроллера, который надо отобразить и флаг использовать анимацию при отображении контроллера или нет. Если вы сейчас запустите приложение, заполните поля “Имя пользователя” и “Пароль пользователя” и нажмете кнопку “Вход”, вы получите ошибку, так как текущее представление не содержит экземпляра “UINavigationController”. Давайте это исправим. Откройте модуль “Main.storyboard”. Выделите в дизайнере представление “ViewController”, затем в меню Xcode выберите “Editor -> Embed In -> Navigation Controller”. После этого действия в дизайнер Storyboard добавится представление “Navigation Controller” привязанное к представлению “ViewController”. Данное представление инициализирует свойство “navigationController” класса “ViewController”, данное свойство имеет тип доступа только для чтения и не подлежит переинициализации в коде контроллера. Модуль “Main.storyboard” должен выглядеть так: Теперь запустите приложение. Заполните поля “Имя пользователя” и “Пароль пользователя”, нажмите кнопку “Вход”. Откроется табличное представление:   Но в нем нас смущает “Top Bar”, который отображается сверху. Его не должно быть видно на странице входа в систему. Чтобы его убрать, откройте модуль “Main.storyboard”, выделите представление “ViewController”, в панели свойств перейдите на вкладку “Show the attributes inspector”. На этой вкладке найдите свойство “Top Bar” и установите для него значение “None”, в дизайнере “Top Bar” пропал, но вот если вы запустите приложение снова, то он остался на прежнем месте. Чтобы его скрыть, перейдите в модуль “ViewController.swift”. После переопределенного метода “viewDidLoad” вам необходимо переопределить еще два метода класса “UIViewController” это методы “viewWillAppear” и “viewWillDisappear”. Исправьте код в соответствии с кодом ниже: Разберем построчно внесенные изменения. На 33 строке мы переопределили метод “viewWillAppear”. Данный метод вызывается когда наше представление уже появилось на экране. На 34 строке мы вызываем метод “viewWillAppear” из базового класса “UIViewController”, чтобы сохранить его поведение. На 35 строке мы обращаемся к свойству “navigationController” и вызываем от экземпляра этого свойства метод “setNavigationBarHidden”. Данный метод скрывает или отображает “Top Bar” и принимает два аргумента, первый аргумент указывает скрыть или отобразить “Top Bar”, второй указывает, сделать это с анимацией или без. В этом методе мы скрываем “Top Bar”. На 38 строке мы переопределяем метод “viewWillDisappear”. Данный метод вызывается когда наше представление перекрывается другим представлением поверх него. На 39 строке мы вызываем метод “viewWillDisappear” из базового класса “UIViewController”, чтобы сохранить его поведение. На 40 строке мы обращаемся к свойству “navigationController” и вызываем от экземпляра этого свойства метод “setNavigationBarHidden”. В этом методе мы отображаем “Top Bar”. Запустите приложение. Теперь все отображается правильно. С одной стороны да, с другой нет. После того как мы выполнили переход на табличное представление, в нем в левом верхнем углу отображается кнопка навигации “< Back”, но ведь наше приложение на русском. Давайте изменим текст этой кнопки на “<- Назад”. Откройте модуль “SuppliesViewController.swift”. После метода “cellForRowAt indexPath”, добавьте следующий код: Разберем этот код построчно. На 36 строке мы объявили метод с именем “initNavigationButtons”, который ничего не возвращает и ничего не принимает. На 37 строке мы вызываем обращаемся в левом операнде к свойству “navigationItem” в котором содержится экземпляр класса “UINavigationItem”. Данное свойство предоставляет доступ к системным кнопкам навигации. От экземпляра свойства “navigationItem” мы обращаемся к свойству “leftBarButtonItem” в котором содержится экземпляр класса “UIBarButtonItem” инициализированный по умолчанию текстом “< Back”. В правом операнде мы создаем новый экземпляр класса “UIBarButtonItem” и вызываем его инициализатор, который принимает 4 аргумента. Первый аргумент “title” задает текст кнопки навигации. Чтобы в текст вставить специальный символ Unicode, необходимо в меню среды разработки Xcode выбрать “Edit -> Emoji & Symbols”, затем выбрать подходящий символ и нажать на него. Второй аргумент “style” принимает перечисление типа “UIBarButtonItemStyle”, в этом аргументе мы задаем значение по умолчанию. Третий аргумент “target” имеет тип “AnyObject?”, в него мы передаем экземпляр нашего контроллера. Четвертый аргумент “action” имеет тип “Selector?”, в него мы передаем метод обработчик нажатия на кнопку навигации. Обратите внимание на запись “#selector(SuppliesViewController.backToParent)”, в этой записи с помощью выражения “#selector” создается экземпляр типа “Selector”. В скобках мы указываем путь к методу обработчику события в формате “Класс контроллера . метод”. На 40 строке мы объявляем метод обработчик события нажатия по кнопке навигации с именем “backToParent”, который не принимает аргументов и ничего не возвращает. На 41 строке мы обращаемся в правом операнде к свойству “navigationController” и вызывает от экземпляра этого свойства метод “popViewController”. Данный метод скрывает текущее представление и переход на предыдущее представление в иерархии вызова представлений. Принимает один аргумент, который задает стиль открытия предыдущего представления с анимацией или без. Добавьте в метод “viewDidLoad”, вызов метода “initNavigationButtons” (19 строка). Запустите приложение. Вы увидите такой результат: На этом урок подошел к концу. На следующем уроке, мы создадим модель товара. Добавим кнопку добавления нового товара, добавим форму редактирования товара и зададим поведение в табличном представлении на редактирование/удаление товара. После того, как редактирование товаров будет завершено, можно будет приступать к уроку в котором будут разбираться иконка приложения и папка Assets.
Введення в розробку програм під iOS. Частина 1.

Автор: Volodymyr Bozhek

Здравствуйте, дорогие читатели. Данный блок будет продолжением темы “Введение в  разработку под iOS", которую мы затронули ранее. В этом уроке мы: рассмотрим пользовательский интерфейс среды разработки; сделаем форму авторизации для нашего приложения Warehouse; научимся добавлять обработчиков событий на элементы управления. Работу продолжим на примере приложения Warehouse, который мы создавали в первом уроке. В среде разработки Xcode можем видеть две панели. Левая из них - это панель навигации по составляющим проекта: Правая делится на панель свойств и панель библиотеки компонентов. Панель свойств: Библиотека компонентов: Также вы можете использовать набор функций в панели инструментов, который находится сверху: Обратите внимание на кнопки этой панели справа: Нажимая на них, вы будете скрывать или отображать левую, правую, нижнюю панель в среде разработки. Левее находится блок функциональных кнопок для отображения данных выбранного модуля: Кнопка отображения текущего представления модуля. Кнопка отображения связанного модуля с текущим модулем. Применяется для того, чтобы видеть на одной половине экрана код контроллера, а на другой - дизайнер представления.  Кнопка отображения изменений с момента последней заливки данных в репозиторий. Используется, если ваш проект находится под управлением GIT Панель снизу - это панель отладки и логирования: В панели навигации откройте модуль “Main.storyboard”. Обратите внимание на стрелку слева, которая указывает на View. Эту стрелку можно перемещать, зажав ее левой кнопкой мыши. Отмечу, что при включении в настройках вашей OS X эмуляции двухкнопочной мыши важно понимать, что на тачпаде нет понятия нажатия левой или правой кнопки мыши, есть просто нажатие. Данная стрелка означает точку входа в наше приложение. Точка входа привязывается только к одной View, и эта View будет отображаться первой при старте нашего приложения. Исключением может быть только модуль конфигурации предварительной заставки “LaunchScreen.storyboard”, тогда сначала будет появляться заставка с презентацией функций приложения, а затем наша View, на которую была привязана точка входа. Нажмите на View, на панели свойств откройте вкладку “Show the identity inspector”. В поле “Class” содержится класс контроллера, который привязан к нашему представлению (View). Рядом с названием класса “ViewController” есть серая иконка со стрелкой вправо, если вы нажмете на нее, то попадете в модуль контроллера “ViewController”. Вернитесь в модуль “Main.storyboard”. В свойствах дизайнера внизу выберите устройство “iPhone 6s”: Перенесите из библиотеки компонентов два элемента управления “Text Field” и один элемент управления “Button” на View. Выделите элемент управления “Text Field” справа и слева от него будут видны квадратики, потяните за правый квадратик и растяните поле на всю ширину экрана. То же самое проделайте со вторым элементом управления “Text Field” и элементом управления “Button”. У вас должно получится так, как на картинке ниже: Обратите внимание на кнопки внизу дизайнера: Это кнопки для отображения панелей свойств элементов управления. На этих панелях задаются выравнивания элементов управления либо относительно View, на котором они расположены, либо относительно других элементов управления, находящихся с ними по соседству. В терминологии Apple они называются ограничениями (Constraints). Это отдельная и довольно объемная тема для понимания, поэтому пока мы будем пользоваться только одной панелью задания ограничений, более простой для понимания. Выделите первый элемент управления “Text Field”, затем нажмите на кнопку “Pin” в дизайнере: Появится диалог ограничений для данного элемента управления: Выпадающие списки сверху, снизу, слева и справа - это допустимое расстояние элемента управления относительно стен View соответственно. Нам необходимо задать ограничение, чтобы наш элемент управления остался на том же месте, где он сейчас расположен на View , но при этом, в зависимости от ширины экрана устройства, подстраивался под эту ширину. Чтобы этого добиться, снимите галочку с “Constrain to margin”, отметьте галочкой поле “Height”, чтобы высота элемента управления не менялась. Отметьте линии верхняя, левая и правая. У вас должно получиться вот так: Заметьте, кнопка “Add 4 Constraints” стала активной. Нажмите на нее. Теперь проделайте ту же самую операцию для второго элемента управления “Text Field” и элемента управления “Button”. Значения в выпадающих списках не меняйте. Можно запустить приложение и посмотреть, что получилось. Остановите приложение. Откройте модуль “Main.storyboard”. Теперь нам необходимо привести в порядок пользовательский интерфейс и сделать его понятным для пользователя. Выделите первый элемент управления “Text Field”. В панели свойств откройте вкладку “Show the attributes inspector”, на этой вкладке найдите поле “Placeholder”, введите в это поле текст “Введите имя пользователя”. Свойство Placeholder задает текст, который отображается в поле ввода, когда это поле не заполнено. Выделите второй элемент управления “Text Field”, в панели свойств, откройте вкладку “Show the attributes inspector”, на этой вкладке найдите поле “Placeholder”, введите в это поле текст “Введите пароль пользователя”. Затем найдите поле “Secure Text Entry” и отметьте его галочкой. Данное свойство будет отображать звездочки вместо текста, вводимого пользователем, используется для полей ввода паролей. Выделите кнопку “Button”, в панели свойств, откройте вкладку “Show the attributes inspector”, найдите поле без названия с текстом “Button”, удалите этот текст и добавьте текст “Вход”. Найдите поле “Text Color”, установите цвет текста “White Color”. Найдите поле “Background”, установите фон кнопки цветом “Blue Color”. Перетащите из библиотеки компонентов на View под кнопку “Вход” элемент управления “Label”. Растяните его по ширине экрана. Задайте ему такие же ограничения, как мы задавали выше для прошлых элементов управления. Выделите элемент управления “Label”, в панели свойств, откройте вкладку “Show the attributes inspector”. На этой вкладке найдите поле без названия с текстом “Label”. Удалите из него текст, введите текст “Нет ошибок”. Найдите поле “Alignment”, задайте выравнивание текста по центру. Выделите View, в панели свойств, откройте вкладку “Show the attributes inspector”, найдите поле “Background”, установите фон View “Light Green”. У вас должно получиться следующее: Теперь необходимо добавить действие на нажатие кнопки “Вход” и объявить переменные для полей ввода и элемента управления “Label”. Для этого в панели инструментов нажмите кнопку “Show the assistant editor”, экран разделится пополам. Если экран вашего компьютера не позволяет нормально видеть дизайнер и модуль контроллера, скройте панель навигатора и панель свойств. Выделите первый элемент управления “Text Field” в дизайнере, не отводя курсор мыши с этого элемента управления, зажмите кнопку “Ctrl”, нажмите и удерживайте левую кнопку мыши, затем начинайте вести курсор на окно с модулем контроллера, когда курсор окажется между фигурными скобками класса контроллера, отпустите левую кнопку мыши, появится диалоговое окно параметров элемента управления: В данном окне есть несколько полей. Давайте их рассмотрим подробнее. Поле “Connection”,  может принимать несколько вариантов значений, в данном уроке нас будут интересовать всего два значения “Outlet” (используется для создания переменной, связанной с элементом управления) и “Action” (используйте для задания обработчика события на какое либо действие элемента управления). Поле “Name”, сюда вы вводите название вашей переменной, которая будет связана с элементом управления. Поле “Type”, содержит тип элемента управления (название класса элемента управления), можно ввести другой класс, если вы знаете иерархию наследования элемента управления UITextField. Поле “Storage”, содержит вид размещения созданной переменной в памяти операционной системы. Может принимать значения “Weak” или “Strong”. В поле “Name” введите название переменной “userNameTxt” и нажмите кнопку “Connect” или кнопку “Enter” на клавиатуре. В модуле контроллера добавится строчка с названием переменной, которую вы задали. Обратите внимание на черный шарик, находящийся на одном уровне с этой переменной слева в области нумерации строк. Нажмите на него, и в дизайнере выделится элемент управления, к которому привязана данная переменная. Вот пример: Повторите данное действие для остальных элементов управления. Для второго текстового поля  укажите название переменной “passwordTxt”. Для элемента управления “Label” укажите название переменной “validationLabel”. Для кнопки выберите в поле “Connection” значение “Action”, у вас добавится еще одно поле “Event”. В данном поле задается событие, на которые вы хотите назначить обработчик в коде контроллера. По умолчанию установлено событие “Touch Up Inside”, оставьте его как есть. В поле “Type” установлено значение “AnyObject” (Это класс, который может содержать любые типы данных, аналог подобного класса в других языка программирования, например, C# - это класс Object). Измените это значение на тип элемента управления UIButton, поскольку данный объект будет передан в аргументы метода обработчика назначенного события и намного удобнее будет обращаться явно к экземпляру кнопки, чем распаковывать этот экземпляр путем приведения к типу “UIButton” из типа “AnyObject”. В поле “Name” введите “loginButtonTapped”. У вас должно получиться вот так: Нажмите кнопку “Connect”. Код контроллера должен выглядеть вот так: Теперь давайте проверим, работает ли событие нажатия по кнопке “Вход”. Добавьте в метод “loginButtonTapped” следующий код: Давайте разберем этот код. На 16 строке идет проверка наличия текста в поле ввода “Имя пользователя”. Ключевое слово “self” означает ссылка на самого себя, в других языках программирования аналог, например, в C# - это ключевое слово “this”. Из переменной “userNameTxt” мы обращаемся к свойству “text”, данное свойство возвращает текст, который вы ввели в данный элемент управления. Знак вопроса в конце  этого свойства ставится, так как это свойство имеет тип String? и может содержать значение “nil” (аналог “null” в C#). Далее мы обращаемся к свойству “characters” , которое содержит массив символов и от этого массива вызываем свойство “count”, чтобы получить количество символов в строке. Такая запись введена, начиная со Swift 3, доступного с обновлением среды разработки до Xcode 8 версии. В ранних версиях Swift, например, версии 2.1-2.3 можно было получить длину строки намного короче “self.userNameTxt.text?.length”. Если поле ввода “Имя пользователя” не заполнено, мы отображаем сообщение валидации “Имя пользователя не задано”. На 18 строке мы делаем аналогичную проверку для поля “Пароль пользователя”. На 20 строке мы проверяем, если заполнены оба поля ввода, тогда все хорошо и мы выводим сообщение “Нет ошибок”. Обратите внимание на запись “(self.userNameTxt.text?.characters.count)!”, скобки и знак восклицания в конце, на самом деле, применяются к свойству “text”, так как оно имеется Nullable тип. Этой записью мы гарантируем компилятору, что в данном свойстве не будет содержаться значение “nil” и данное свойство будет инициализировано. Теперь запустите приложение и проверьте его работу. На этом урок подошел к концу. В следующем уроке мы рассмотрим работу с классами UITableViewController, навигацию между представлениями, понятие делегата, разберем формат и размеры иконки приложения, научимся использовать Assets.
Введення в розробку програм під iOS. Частина 0.

Автор: Volodymyr Bozhek

Здравствуйте, дорогие читатели. Меня зовут Владимир. Любая технология рано или поздно приедается и рост себя как специалиста затрудняется. Было принято решение что то кардинально поменять в своей жизни. А так как писать различные интересные проекты я просто обожаю, было принято решение начать изучение в корне другой платформы, среды разработки и языка программирования. Поиск этой платформы оказался достаточно прост для меня. Я решил, что это будет мобильная платформа. У меня было много разных телефонов с разными операционными системами. Но свой выбор я сделал в пользу iOS, так как при железе слабее, чем у других телефонов, софт работает намного быстрее, чем на других платформах. Это не все преимущества этой платформы, но о других я рассказывать я не буду, дабы не раздувать дискуссию на эту тему. Скажу лишь одно, что телефон c iOS меня ни разу не подвел, даже в самые нужные моменты. Разумеется, что для разработки под iOS необходим mac, и я его купил. Перед покупкой, разумеется, пробовал разработку под мобильные платформы в гибридных платформах типа Xamarin, Ionic, но на то время функционал в них был не так совершенен, как сейчас, да и пользуясь ими, я бы остался на платформе Windows, это нарушало цель, которую я перед собой поставил по поводу другой платформы. В течение отпуска, где то за месяц я разобрался, как писать под iOS приложения и сразу начал искать проект, на котором смогу потренироваться. Друг предложил такой проект и я взялся. За 9 месяцев написал серьезное приложение, купил учетную запись разработчика, выкладывал сборки на Test Flight, их тестировали тестировщики, прошел валидацию в App Store и выложил приложение в App Store. А теперь по сути. Я постараюсь написать серию статей, в которых распишу простым языком, что надо делать, чтобы у вас тоже это получилось и вы таки выложили свое приложение в App Store. Касаемо разработки под iOS, то это просто мое хобби во вне рабочее время (надо же как то развлекаться), основной хлеб приносит разработка под Windows/Web платформу с технологиями Microsoft. Итак, приступим. Включите свой мак, найдите на панели задач иконку App Store и нажмите на нее. Выглядит эта иконка вот так: Затем введите в поле поиска, текст “xcode”, вы должны увидеть следующее:  У меня Xcode уже установлен, поэтому на скриншоте отображается кнопка “Open”, у вас будет отображаться кнопка “Install”. Установите Xcode себе на компьютер, версия Xcode с которой мы будет работать с вами в этом примере, будет 8, на текущий момент это последняя версия этой среды разработки. Язык программирования, который мы будем использовать, называется Swift, по синтаксису он больше похож на JavaScript и где-то даже на C# местами. Синтаксис языка Swift в этих статьях мы разбирать не будем, в сети интернет есть полно руководств на эту тему. Сам синтаксис языка не сложно изучить, это делается за день, сложность как раз возникнет при попытке разработки приложения под iOS. Вот именно эту сложность, мы с вами и постараемся одолеть. Чтобы серия статей была как-то взаимосвязана, мы с вами напишем проект под названием “Warehouse” (склад), в котором можно будет добавлять товары, заполнять их атрибуты, редактировать и удалять товары. Данные будут сохраняться в настройки телефона. Вы уже установили Xcode 8? Если да, тогда запускайте его, вы увидите следующее: Нажмите кнопку “Create a new Xcode project”. Вы увидите такой экран:  Выделите проект “Single View Application” и нажмите кнопку “Next”. Вы попадете на экран задания названия проекта: На данном скриншоте виден пример того, как заполнить название приложения. В поле “Product Name“, введите “Warehouse”, так будет называться наше приложение. В поле “Organization Name”, введите свои имя и фамилию, или название вашей компании. В поле “Organization Identifier”, вводится уникальный идентификатор приложения, который будет использоваться как ключ при регистрации данного приложения в iTunes, чтобы была возможность подписывать сборки и отправлять на бета сервер Test Flight для тестирования, но об этом позже. Вкратце “com” - это сокращенно “company”, “bozhek” это название компании, у вас это будет другое название. В поле Bundle identifier видно полное название вашего приложения в iTunes. В поле Language, оставьте Swift. В поле Devices, выберите iPhone. Нажмите кнопку “Next”. Откроется проект, который мы создали: Обратите внимание на красный кружок в поле Status: “Signing for Warehouse reguires a development team”. Это сообщение говорит нам о том, что приложение надо подписать сертификатом, без сертификата мы не соберем и не запустим это приложение. Давайте это исправим. В поле Team, откройте выпадающий список и выберите в нем пункт “Add an Account...”, откроются свойства среды разработки Xcode, с предложением ввести логин и пароль от  вашей учетной записи в App Store, под которой вы устанавливаете приложения себе на телефон: Если же у вас по каким то причинам нет этой учетной записи, создайте ее, нажав на кнопку “Create Apple ID”. Создание этой учетной записи я не буду рассматривать в данной статье, поскольку она создается очень легко и в сети интернет есть полно информации на эту тему. Итак, вы ввели в поля Apple ID и Password свои данные, нажмите кнопку “Sign In”. После этого красный круг с ошибкой пропадет и вы увидите следующее: Теперь вы сможете собрать и запустить свой проект. Заметьте, я рассказываю чисто практически как и что делается, особо не вникая в детали. Если вам нужна более подробная информация, вбейте в гугл WWDC, и посмотрите официальные видео сессии от компании Apple, в их двухчасовом видео много воды, но зато есть теория, которая будет вам нужна. Я даю только суть. Нажмите на иконку приложения в проекте: Выберите симулятор “iPhone 7”. Затем нажмите на кнопку Собрать и запустить приложение: Будет запущена сборка проекта и запущен симулятор “iPhone 7”, в котором откроется наше приложение: Сейчас мы видим только белый экран и больше ничего, так как мы еще ничего не делали. Чтобы выйти на рабочий стол в симуляторе, выполните сочетание клавиш Shift + Command + H, это сочетание означает нажатие кнопки Home: Вы увидите на рабочем столе наше приложение "Warehouse". Теперь давайте остановим выполнение приложения, для этого в Xcode нажмите кнопку: Теперь давайте посмотрим, что у нас есть внутри проекта. Выделите в левой панели среды разработки файл “Main.storyboard”, в этом файле содержится представление приложения. В панели справа отображаются свойства представления. Разработка под iOS строится через паттерн MVC (Model View Controller), файл Main.storyboard, содержит View, которые вы будете использовать в своем приложении для создания визуального  интерфейса. Теперь давайте выделим в панели свойств вкладку Class: В поле Class , мы видим класс контроллера, привязанный к данному представлению. Теперь выделите вкладку Size: На данной вкладке задаются размеры представления. Выделите вкладку Events: На данной вкладке задаются обработчики событий в представлении с привязкой к методам в контроллере. Теперь выберите в левой панели файл ViewController.swift : На 9 строке подключено пространство имен UIKit. На 11 строке объявляется класс с именем ViewController, который наследуется от базового класса контроллера UIViewController. На 13 строке мы переопределяем метод, загрузки представления, этот метод вызывается когда представление начинает свою загрузку. В этом методе необходимо предварительно проинициализировать данные элементов управления, используемых на вашем представлении. На строке 14, вызывается метод базового класса ViewController. Код на строке 18 нам пока не интересен, мы его рассматривать не будем, чтобы не путаться. На этом урок завершается. На следующем уроке, в первой части, мы рассмотрим с вами, как пользоваться элементами управления, как привязывать к ним обработчики событий и как взаимодействовать с ними.
Як я побудував проект на Django, Django REST Framework, Angular 1.1.x та Webpack

Автор: Редакция ITVDN

Моя идея состояла в том, чтобы построить простой репликабельный проект на Angular с бэкэндом на Django. Я искал и не смог найти нужных решений, пришлось во всем разбираться самому. В итоге я разобрался и решил сам написать гайд для всех, кого может заинтересовать данная проблема.  Данная статья поможет вам построить простое приложение Angular с бэкэндом на Django, организованного с помощью Webpack. Проблема Я хочу настроить проект на Angular 1.1.x и скормить ему данные с сервера Django. Мне бы хотелось использовать Django REST Framework (DRF), чтобы пострить RESTful API. Я также хочу сбандлить JS ассеты. Сейчас я собираюсь запустить сайт на одном сервере. Предварительные требования Python 2.x Django 1.9.x npm 2.15.8+ Webpack 1.13.x (sudo npm i -g webpack) ESLint 2.13.1+ (sudo npm i -g eslint) NodeJS 4.4.7+ Содержание Скаффолдинг проекта. Создайте свои начальные директории. Скаффолдинг проекта на Django. Настрока переменных среды, нужных для запуска сервера Django. Установка Django REST Framework и настройка Django с использованием переменных среды. Создание API. Запуск Django сервера с использованием dev settings. Инициализация npm-пакета и установка front-end JS зависимостей. Создание Angular entry-point и загрузка начальных зависимостей. Настройка Webpack'а. Дайте команду Django загрузить приложение. Создайте шаблон базы приложения Angular. Напишите компонент home. Напишите Angular роуты, ведущие к вашему компоненту home и странице 404. Добавьте директивы ангуляр-маршрутизатора к шаблону входной точки приложения. Проверьте ваше REST API в приложении Angular.  Шпаргалка. Итак, начнем! 0. Настройте среду для Python. mkvirtualenv mysite 1. Скаффолдинг проекта на Django. Создайте начальные директории. Мы хотим сфокусироваться на модулярности в ходе разработки. Следовательно, существует множество директорий в конечном итоге использования. Мы хотим, чтобы наше дерево изначально выглядело так: mysite ├── backend │ ├── docs │ ├── requirements └── frontend ├── app │ ├── components │ └── shared ├── assets │ ├── css │ ├── img │ ├── js │ └── libs ├── config ├── dist └── js  Сделайте следующее: mkdir mysite && cd mysite mkdir -p backend/docs/ backend/requirements/ \ frontend/app/shared/ \ frontend/app/components/ \ frontend/config \ frontend/assets/img/ frontend/assets/css/ \ frontend/assets/js/ frontend/assets/libs/ \ frontend/dist/js/  *Примечание: Структура этого проекта была навеяна опытом с несколькими другими проектами. Я считаю эту организацию идеальной, но вам не обязательно ей следовать. Но, пока вы читаете этот гайд, вы должны придерживаться этой структуры. 2. Скаффолдинг проекта на Django. В директории backend/ создайте Django проект: python django-admin.py startproject mysite  Также создайте requirements.txt: pip freeze > requirements/requirements.txt В директории (вашего проекта) backend/mysite/ произведите скаффолдинг директории, той, где будет жить ваше API: mkdir -p applications/api/v1/ touch applications/__init__.py applications/api/__init__.py \ applications/api/v1/__init__.py applications/api/v1/routes.py \ applications/api/v1/serializers.py applications/api/v1/viewsets.py Теперь создайте структуру директории настроек: mkdir -p configlord/settings/ touch configlord/settings/__init__.py \ configlord/settings/base.py configlord/settings/dev.py configlord/settings/prod.py \ configlord/dev.env configlord/prod.en 3. Настройте переменные окружения, которые нужны для запуска сервера Django. На этом этапе я предпочитаю пользоваться django-environ для работы с переменными окружения. Существует множество способов сделать это, но пакет django-environ чрезвычайно упрощает этот процесс, поэтому я использую его во всех своих проектах. Установите django-environ: pip install django-environ  В mysite/dev.env добавьте следующее: DATABASE_URL=sqlite:///mysite.db DEBUG=True FRONTEND_ROOT=path/to/mysite/frontend/ SECRET_KEY=_some_secret_key Мы собираемся использовать эти переменные среды в наших настройках. Выгода от использования наших переменных окружения в отдельных файлах состоит в основном в том, что такая настройка позволяет облегчить переключение между средами. В нашем случае файл the dev.env является списком переменных, которые мы бы использовали в локальной среде разработки. *Примечание: SECRET_KEY можно взять из settings.py, который был сгенерирован django-admin.py startproject. 4. Установите Django REST Framework и настройте Django, используя переменные среды. Установка DRF: pip install djangorestframework Наполните settings/base.py  следующим: Укажите, где искать переменные окружения. import environ project_root = environ.Path(__file__) - 3 env = environ.Env(DEBUG=(bool, False),) CURRENT_ENV = 'dev' # 'dev' is the default environment # read the .env file associated with the settings that're loaded env.read_env('./mysite/{}.env'.format(CURRENT_ENV)) Установите базу данных. В данном случае мы собираемся использовать встроенные в django-environ настройки SQLite. DATABASES = { 'default': env.db() } Установите SECRET_KEY ,а также debug. SECRET_KEY = env('SECRET_KEY') DEBUG = env('DEBUG') Добавьте DRF в пул приложений, которые Django должен использовать. # Application definition INSTALLED_APPS = [ ... # Django Packages 'rest_framework', ] Ссылки будут «жить» в специальном URL модуле, созданном с помощью базы проекта. ROOT_URLCONF = 'mysite.urls' Укажите Django, где искать все шаблоны и другие статические ассеты. STATIC_URL = '/static/' STATICFILES_FINDERS = [ 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', ] STATICFILES_DIRS = [ env('FRONTEND_ROOT') ] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [env('FRONTEND_ROOT')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] В соответствии с настройкой TEMPLATES Django должен будет искать шаблоны внутри frontend/ directory. Это то, где Angular приложение будет жить. Мы используем только Django, чтобы обслужить шаблон, внутри которого Angular приложение будет загружаться, которое будет выполнено через entry-point директиву. Если вы не знаете, о чем я, продолжайте чтение...  Наполните settings/dev.py: from mysite.settings.base import * CURRENT_ENV = 'dev' Здесь мы указываем, что этот файл настроек унаследывает настройки из base.py и переопределяет строку CURRENT_ENV, найденную в base.py. Мы говорим: «Используй это значение вместо значения, найденного в наследуемом модуле». 5. Создайте API. Нам нужно нечто, с помощью чего мы сможем протестировать службы Angular, поэтому давайте создадим небольшое API. Этот шаг можно пропустить, но я не советовал бы делать этого. Нам важно знание того, что настройки приложения Angular работают исключительно с точки зрения его потенциала, чтобы облегчить HTTP запросы. Сгенерируйте приложение. manage.py startapp games Создайте модель в games/models.py. class Game(models.model): title = models.CharField(max_length=255) description = models.CharField(max_length=750) Создайте DRF сериализатор для модели игры в  applications/api/v1/serializers.py. from rest_framework.serializers import ModelSerializer from applications.games.models import Game class GameSerializer(ModelSerializer): class Meta: model = Game Создайте  DRF viewset для модели в приложениях applications/api/v1/viewsets.py. from rest_framework import viewsets from applications.games.models import Game from applications.api.v1.serializers import GameSerializer class GameViewSet(viewsets.ModelViewSet): queryset = Game.objects.all() serializer_class = GameSerializer В applications/api/v1/routes.py зарегистрируйте роуты, используя DRF's router registration features. from rest_framework import routers from applications.api.v1.viewsets import GameViewSet api_router = routers.SimpleRouter() api_router.register('games', GameViewSet) Обозначьте ссылки для зарегистрированного DRF роута внутри mysite/urls.py: from django.contrib import admin from django.conf.urls import include, url from applications.api.v1.routes import api_router urlpatterns = [ url(r'^admin/', admin.site.urls), # API:V1 url(r'^api/v1/', include(api_router.urls)), ] 6. Запустите сервер Django, используя dev settings. manage.py runserver --DJANGO_SETTINGS_MODULE=mysite.settings.dev Впуская DJANGO_SETTINGS_MODULE в runserver, мы «говорим» - работать используя специфические параметры. Если все работает, у вас появится возможность открыть localhost:8000/api/v1/games и увидеть ответ от DRF. Если все работает – самое время заняться построением приложения Angular. Если нет – направьте автору проблему. Если вы застряли на этом этапе – оставьте комментарий автору под оригиналом публикации. 7. Инициализируйте npm-пакет и установите front-end JS зависимости. Приложение Angular не будет работать так, как мы хотим, если правильные зависимости не будут установленны. Самое время установить базовые пакеты, которые понадобятся. Инициализируйте npm-пакет. Прямо из  frontend/ запустите npm init --yes By passing the --yes flag into init, you're telling NPM to generate a package.json using NPM defaults. Otherwise, if you don't pass that in, you'll have to answer questions... Boring. Установите dev dependencies.  npm install --save-dev eslint eslint-loader Установите общие зависимости. npm install --save eslint eslint-loader angular angular-resource angular-route json-loader mustache-loader lodash Файл package.json file во frontend/ должен выглядеть приблизительно следующим образом: { "name": "my-app", "version": "0.0.1", "description": "This is my first angular app.", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "eslint": "^3.1.1", "eslint-loader": "^1.4.1" }, "dependencies": { "angular": "^1.5.8", "angular-resource": "^1.5.8", "angular-route": "^1.5.8", "eslint": "^3.1.1", "eslint-loader": "^1.4.1", "json-loader": "^0.5.4", "lodash": "^4.13.1", "mustache-loader": "^0.3.1" } } Здесь то, что мы только что установили: eslint – отличный линтер, благодаря которому код JavaScript будет в порядке (последователен). eslint-loader – для запуска eslint через Webpack. Чуть позже я объясню концепцию «загрузчиков». angular -  MVC фреймворк. Если  вы не знали об этом, стоит подумать о том, чтобы закрыть эту страничку прямо сейчас. angular-resource -  (Angular) HTTP библиотека выбора. Это абстракция $http. json-loader - загрузчик (снова, используемый Webpack) для распаковки JSON из .json файлов с помощью require() во время работы нашего приложения. mustache-loader – загрузчик, который мы будем использовать, чтобы парсить наши mustache шаблоны. Mustache шаблоны – это веселье. Я могу спокойно предположить, что вы не знаете, как все эти пакеты заиграют вместе.  Не переживайте, братишки. 8. Создайте entry-point в Angular, объявите начальные зависимости, объявите первоначальные глобальные переменные. В frontend/app/app.js добавьте следующее: /* Libs */ require("angular/angular"); require("angular-route/angular-route"); require("angular-resource/angular-resource"); /* Globals */ _ = require("lodash"); _urlPrefixes = { API: "api/v1/", TEMPLATES: "static/app/" }; /* Components */ /* App Dependencies */ angular.module("myApp", [ "ngResource", "ngRoute", ]); /* Config Vars */ // @TODO in Step 13. /* App Config */ angular.module("myApp").config(routesConfig); app.js это то, где Webpack будет искать модули, чтобы бандлить их вместе. Лично я ценю такую организацию и методику вызовов, но такой порядок не обязателен. Существует 6 секций: Libs – главные библиотеки, используемые на протяжении работы Angular приложения; Globals – зарезервированные глобальные переменные, которые мы можем использовать во время работы приложения; Components (Компоненты) – особенные модули проекта; App Dependencies (Зависимости приложения) – объявление входной точки приложения и его зависимостей; Config Vars – переменные, где хранятся настройки, такие как route config; App Config - вводит configs (настройки) в приложение, используя сохраненные из предыдущей секции. Для того, чтобы globals работали, вам следует указать ESLint на то, какие из переменных - глобальные. В config/eslint.json добавляем следующее: { "env": { "node": true }, "extends": "eslint:recommended", "rules": { "indent": [ "error", 2 ], "linebreak-style": [ "error", "unix" ], "quotes": [ "error", "double" ], "semi": [ "error", "always" ], "no-console": 0 }, "globals": { "_": true, "_urlPrefixes": true, "angular": true, "inject": true, "window": true }, "colors": true } Ниже несколько переменных, о которых мы предупредили ESLint: _ представить lodash. _urlPrefixes – объект, который мы будем использовать в приложении для гиперссылок. Я расскажу об этом позже. angular, чтобы представить AngularJS object driving our entire application. inject, который будет использоваться для ввода зависимостей Angular. window, которая просто представляет объекты окон в JavaScript, является представителем  DOM. 9. Настройка Webpack. Теперь, когда мы выложили большинство наших зависимостей приложения, мы можем построить config file для Webpack. Webpack будет консолидировать все зависимости, а также модули для приложений, которые мы создаем в один файл. В bundle. В frontend/webpack.config.js добавляем следующее. module.exports = { entry: "./app/app.js", output: { path: "./dist/js/", filename: "bundle.js", sourceMapFilename: "bundle.js.map", }, watch: true, // eslint config eslint: { configFile: './config/eslint.json' }, module: { preLoaders: [{ test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader" }], loaders: [ { test: /\.css$/, loader: "style!css" }, { test: /\.html$/, loader: "mustache-loader" }, { test: /\.json$/, loader: "json-loader" }] }, resolve: { extensions: ['', '.js'] } }; Для того, чтобы Webpack бандлил все наши статические зависимости, нам нужно указать ему, где их брать, какие зависимости обрабатывать и как управлять ими до банлинга. Давайте посмотрим на то, что указывает Webpack с помощью webpack.config.js: Entry - это путь к тому, что Webpack'у нужно для старта бандлинга. Это можеть быть полный путь или путь, относительный тому, где webpack.config.js располагается. В данном случае мы говорим о последнем варианте. output -  это объект, содержащий в себе path, который является директорией, в которую связанные зависимости будут помещаться; filename - это название бандла; и, в данном случае, мы решили использовать sourceMapFilename, чтобы обозначить, что наша() source map будет вызван(а). watch указывает Webpack следить за изменениями в файле, пока он выполняется. Если это не настроено как true, Webpack прогонит процесс бандлинга единожды и остановится. eslint содержит в себе специфические ESLint настройки, используемые eslint-loader. module указывает Webpack'у, что делать с модулями, с которыми он работает. module.preLoaders «говорит», что делать перед бандлингом. В данном случае мы хотим запустить модули (исключив модули установленные npm) через eslint. module.loaders - это то, где указана последовательность загрузчика. В нашем случае мы просто настраиваем test и loader,  где test указывает Webpack’у, какие модули запускать в загрузчике (по соответствию с паттерном regex), и loader говорит Webpack’y, какой загрузчик использовать в модулях, которые соответствуют regex паттерну в test. Каждый загрузчик указан в строке и разделен восклицательным знаком. Ex:  loader!another_loader!yet_another_loader module.preLoaders указывает, какие preLoaders'у запускать модули. Используемые настройки такие же точно, какие мы использовали в module.loaders. Но, Грег, какая  разница между preLoaders и loaders? Я рад, что ты спросил, мой дорогой друг!! A loader указывает Webpack'у, как бандлить требуемые файлы. Loader смотрит на модуль и говорт: «Эй, так как вы упаковываете это в один файл как строку – это то, как оно должно быть преобразованно для bundle'а». A preLoader обрабатывает код перед loaders, например, чтобы слинтить JavaScript модули. A postLoader является плагином Webpack'а, который обрабатывает код после бандинга. Мы не специфицировали ни один  postLoader ради простоты. 10. Укажите Django загрузить приложение. Прямо сейчас все, что нужно сделать – указать Webpack’у что создавать, как создавать и что должно быть создано. (На данном этапе я бы очень удивился, если вы попробуете запустить его и он заработает без ошибок. Если так и есть, я чертов мужик.) Так как Django использует свой собственный URL процессор в нашем приложении, мы можем быть рады тому, как любезно Django управляет всем тем, что введено в строку браузера пользователя. Как бы то ни было, мы бандлим одностраничное приложение, используя абсолютно другой фреймворк, и хотим, чтобы у приложения был полный контроль над тем, что пользователь вводит. Все, что нам нужно  – обслуживать одну страничку, в которой работает SPA. Следовательно... В backend/mysite/mysite/urls.py добавляем в список urlpatterns следующее: # Web App Entry url(r'^$', TemplateView.as_view(template_name="app/index.html"), name='index'), Это значит, что когда пользователь открывает mysite.com/, env('FRONTEND_ROOT') + app/index.html будет находить STATICFILES_FINDERS  в порядке рендера HTML шаблона. 11. Создайте шаблон базы приложения Angular. frontend/app/components/app/index.html шаблон должен выглядеть как обычный шаблон Django. В frontend/app/index.html добавляем следующее: {% load staticfiles %} <html ng-app="myApp"> <head> <title>My Sitetitle> <script src="{% static 'dist/js/bundle.js' %}">script> head> <body> body> html> В таком случае вам удастся запустить Webpack. Если вы запустите Django сервер и откроете localhost:8000,вы увидите пустую страничку. Если нет – дайте знать автору. 12. Напишите home component. Давайте напишем наш первый компонент. Он отобразит текст на страничке, пока пользователь открывает localhost:8000. Создайте директорию для компонента и базовые файлы. В frontend/app/components/: mkdir home && touch home/home-controller.js home/home.js home/home.html В frontend/app/components/home/home.html добавляем следующее: <div ng-controller="HomeController as ctrl"> <div> <h1>Home!h1> div> div> Теперь добавим следующее в  frontend/app/components/home/home-controller.js: function HomeController() { var that = this; that.foo = "Foo!"; console.log(that); // should print out the controller object } angular.module("Home") .controller("HomeController", [ HomeController ]) Определение модуля Angular должно быть объявлено в home.js: angular.module("Home", []); require("./home-controller"); Теперь мы можем сослаться на "Home" в области зависимости определения модуля. Давайте сделаем это! В app/app.js добавьте следующее: /* Components */ require("./components/home/home"); /* App Dependencies */ angular.module("myApp", [ "Home", // this is our component "ngResource", "ngRoute" ]); 13. Пропишите пути Angular'а, ведущие к home component и страничке 404. Нам нужно настроить первый путь. Когда пользователь попадает на localhost:8000, Angular должен взять контроль над загрузкой отрендеренного шаблона. Чтобы сделать это, нам потребуется использовать angular-router. В frontend/app/routes.js пишем следующее: function routesConfig($routeProvider) { $routeProvider .when("/", { templateUrl: _urlPrefixes.TEMPLATES + "components/home/home.html", label: "Home" }) .otherwise({ templateUrl: _urlPrefixes.TEMPLATES + "404.html" }); } routesConfig.$inject = ["$routeProvider"]; module.exports = routesConfig; Если мы не добавим _urlPrefixes.TEMPLATES, angular-router предположит, что components/home/home.html является действительной ссылкой, которую узнает сервер. Так как STATIC_URL в настройках предполагает неправильную работу localhost:8000/components/home/home.html. Также, если вы еще не заметили, вы увидите otherwise({...})  в коде роутов. Это то, как будут реализованы страницы 404. В frontend/app/404.html добавляем следующее: <h1>NOT FOUNDh1> И в завершении  добавляем frontend/app/app.js: /* Config Vars */ var routesConfig = require("./routes"); 14. Добавьте директивы angular-router к шаблону точки входа приложения. А теперь нам нужно указать Angular, где будет происходить переключение отображаемого, когда пользователь пользуется навигацией. Чтобы сделать это, мы используем всю силу angular-router. В тэг    в frontend/app/index.html добавляем: <base href="/"> Теперь в тэг  добавляем: <div ng-view>div> Ваш index.html теперь должен выглядеть так: {% load staticfiles %} <html ng-app="myApp"> <head> <title>My Sitetitle> <script src="{% static 'dist/js/bundle.js' %}" >script> <base href="/"> head> <body> <div> <div ng-view>div> div> body> html> Запустите Webpack. Откройте localhost:8000. Вы должны увидеть, что произошло в home/home.html. (Если ничего, отправьте эти данные автору J ). 15. Проверьте REST API в приложении Angular. Если все сделано, у вас появится возможность написать angular службы для Django API. Давайте создадим небольшой компонент, чтобы увидеть, можем ли мы это сделать. Этот компонент должен перечислять игры. Я предполагаю, что вы уже заполнили базы данных, следовательно запрос HTTP к localhost:8000/api/v1/games вернет список игр. Создайте скаффолд компонент в frontend/app/components/: mkdir -p game/list/ && touch game/list/game-list-controller.js game/list/game-list-controller_test.js game/game-service.js game/game.js game/game.html Этот компонент будет перечислять игры. Этот компонент должен перечислять игры. Я предполагаю, что вы уже заполнили базы данных, следовательно запрос HTTP к localhost:8000/api/v1/games вернет список игр. В game/game-service.js: function GameService($resource) { /** * @name GameService * * @description * A service providing game data. */ var that = this; /** * A resource for retrieving game data. */ that.GameResource = $resource(_urlPrefixes.API + "games/:game_id/"); /** * A convenience method for retrieving Game objects. * Retrieval is done via a GET request to the ../games/ endpoint. * @param {object} params - the query string object used for a GET request to ../games/ endpoint * @returns {object} $promise - a promise containing game-related data */ that.getGames = function(params) { return that.GameResource.query(params).$promise; }; } angular.module("Game") .service("GameService", ["$resource", GameService]); Обратите внимание на ссылку $resource, которую мы используем для того, чтобы настроить механизмы HTTP в нашей службе. В game/list/game-list-controller.js: function GameListController(GameService) { var that = this; /* Stored game objects. */ that.games = []; /** * Initialize the game list controller. */ that.init = function() { return GameService.getGames().then(function(games) { that.games = games; }); }; } angular.module("Game") .controller("GameListController", [ "GameService", GameListController ]); В game/game.html: <div ng-controller="GameListController as ctrl" ng-init="ctrl.init()"> <div> <h1>Gamesh1> <ul> <li ng-repeat="game in ctrl.games">{{ game.title }}li> ul> div> div> В game/game.js: angular.module("Game", []); require("./list/game-list-controller"); require("./game-service"); Затем обратимся к компоненту в app.js: /* Components */ require("./components/game/game"); /* App Dependencies */ angular.module("myApp", [ "Home", "Game", "ngResource", "ngRoute" ]); В конце концов, мы собираемся настроить роуты для списка игр, поэтому в frontend/app/routes.js добавьте следующее в объект $routeProvider: .when("/game", { templateUrl: _urlPrefixes.TEMPLATES + "components/game/list/game-list.html", label: "Games" }) Запустите Webpack снова. Все должно верно скомпилироваться. Если нет – дайте знать автору. Откройте localhost:8000/#/games. Вы увидите список игр. Сделано! Это все. Сомнения/Мысли Но есть некоторые сомнения: Глобальные переменные могут конкретно подставить вас, если вы не знаете, как с ними работать. Их локальное поведение не гарантирует того же на продакшене. Насколько я помню, их можно заставить работать, если правильно описан метод. Ваше приложение на Angular тесно связанно с Django. Поэтому ваше приложение не будет просто слиянием back- и фронтенда. Если ваш Django-RIP давно устарел, значит поменялись и маршруты, следовательно сконфигурируете ваш бэкенд согласно тому, как должны вести себя статические файлы. Так же вам будет необходимо заменить index.html с точкой входа Angular. Маленькие проекты не дадут вам особо попотеть, а вот большие явно заставят понервничать. Совет: единственное место, где должны сопрягаться приложение на Angular и Django сервер - это одна точка входа. Деплоймент должен быть выполнен так же, как любой обычный деплоймент приложения. Это все. Если у вас есть какие-либо вопросы и вы испытываете трудности, пожалуйста, оставьте их в комментариях в исходной статье! Чит! Автор пообещал выложить на гитхабе репозиторий со всем кодом. Оригинальная статья на английском языке. 
Паралакс для 2D гри без нервів та милиць

Автор: Дар'я Коновалова

Недавно в моей жизни начинающего разработчика игр появилась задача - сделать фон в игре, но не просто уныленький статичный бэкграунд, а параллакс. Да, эта чудо-красота применима не только в разработке сайтов, но и при создании игр. Попытка вдохновиться в гугле практически ничем не закончилась. Пришлось справляться с задачей собственными силами. У меня получилось. Хочу поделиться с вами опытом. Справедливо замечу, что параллакс — это не достояние веба. Еще в дремучие времена существования 8-битных игр параллакс успешно применялся для создания иллюзии объема в двухмерной игре. Коротко говоря, параллакс — это наслоение изображений, каждый слой движется со своей скоростью. Ближайший к игроку имеет самую высокую скорость, соответственно дальний (последний) — самую низкую. Ну что, вроде, минимально в теории разобрались и даже нашли, откуда ноги растут, значит, мы готовы перейти непосредственно к практике и сотворить это чудо своими руками. Что вам потребуется: базовые знания Unity3D 5 (на уровне создания скриптов, добавления компонентов); понимание С#; 3 или больше картинок в формате .png; внимательность и желание. Ладно, последнее не очень обязательно =) По ссылке вы можете скачать необходимые изображения, а также уже готовый проект. Подготовка Запускаем Unity3D, создаем новый проект, называем его, например, Parallax2D. Закидываем в папку Assets наши бэкграунды. Рекомендую сложить их в отдельную папку. В моем случае они лежат в Assets – StarSky. Каждое изображение называем удобно и понятно. Я назвала их по порядку размещения (Background – задний фон, MiddleBackground – средний, TopBackground – верхний слой). Для того, чтобы картинка перемещалась гладко, нам необходимо настроить ее в Inspector. Обратите внимание, этот этап очень важен, иначе все размажет, как звезды за иллюминатором Энтерпрайза на 3-й космической скорости. В поле Texture Type выбираем тип Texture, во Wrap Mode отмечаем Repeat. И радостно тыкаем Apply. Без этого действия изменения не сохранятся, а потом можно долго недоумевать, почему же оно не работает. Совершаем эти телодвижения и для 2-х остальных текстур. Подготовив картинки, переходим к этапу размещения их на сцене. Часто в этих ваших интерентах можно встретить совет - размещать их с помощью GameObject – Plane. Вы, конечно, можете потрудиться и заставить 3D объект нормально функционировать в 2D игре. Но, на самом деле, это все будет уныло, как последний эпизод «Звездных войн», а работать это чудовище будет чуть более быстро, чем аж никак. Поэтому я рекомендую долго не мучиться и использовать элемент UI – Canvas. Canvas меняет размер фона автоматически, подстраивает его под размеры экрана гаджета, на котором запускают игру. Это избавляет нас от потребности писать скрипт, который будет отвечать за отслеживание размеров экрана и изменения размера фона. В Hierarchy выбираем UI – Canvas. Собственно, если работать с Юнькой для вас не впервой, то вы явно знаете еще много других способов, как добавить в Hierarchy объект. Так что делайте это любым удобным способом. Создав Canvas, добавляем чайлдами («внутрь» канвы) три Panel для 3-х наших фонов. После добавления наша Hierarchy выглядит так: Переименовываем Canvas и Panel, чтобы у всех были свои пароли и явки. А теперь засучиваем рукава и беремся препарировать — подготавливать каждый компонент в Inspector. Начнем с ParallaxBackground. Изменяем Layer на Ignore Raycast, чтобы наш фон не реагировал на касания пальцами. Unity обязательно спросит, применить ли эти изменения ко всем «детям» — соглашаемся.  Далее переходим к компоненту Canvas. Находим Render Mode, выбираем Screen Space – Camera. В Render Camera добавляем нашу текущую дефолтную камеру (Main Camera(Camera)). Plane Distance ставим пока 110, но этот показатель проще отстроить во время теста. По факту — это расстояние от камеры до нашей канвы. То есть, изменяя его, мы будем получать разную глубину изображения. Остальное не трогаем и переходим к Back. В Rect Transform привязываем позицию к левому краю. Теперь наш фон будет всегда отстраиваться по одному краю, и мы избежим проблем с правильной позицией на разных устройствах. Удаляем компонент Image (Script), вместо него добавляем Raw Image (Script) (напомню, Add Component – UI – Raw Image (Scriot)). В Texture добавляем картинку нашего самого последнего слоя. Те же операции проделываем и для остальных слоев. Можно сделать немного проще, наколдовать изменения в первой Panel, дублировать (Ctrl + D), поставить в каждую свою текстуру, переименовать. Тут уже зависит от того, как вам будет удобнее. Запускаем сцену — любуемся. Три картинки прекрасно легли друг на друга. Немного черной магии Теперь весь смак. Мы с вами напишем скрипт, который заставит наши картинки двигаться. Создаем новый скрипт, напомню, пишем на C#, назовем его BackgroundHelper. Он у нас будет один, поэтому нет смысла делать отдельную папку, кидаем его прямо в основную Assets. Открываем созданный скрипт и понеслась тяжкая работа на 5 строчек: using UnityEngine; using UnityEngine.UI; // обязательно добавляем библиотеку пользовательского интерфейса, без нее кино не будет using System.Collections; public class BackgroundHelper : MonoBehaviour {     public float speed = 0; //эта публичная переменная отобразится в инспекторе, там же мы ее можем и менять. Это очень удобно, чтобы настроить скорость разным слоям картинки     float pos = 0; //переменная для позиции картинки     private RawImage image; //создаем объект нашей картинки               void Start () {         image = GetComponent<RawImage>();//в старте получаем ссылку на картинку         }                    void Update () {         //в апдейте прописываем как, с какой скоростью и куда мы будем двигать нашу картинку           pos += speed;             if (pos > 1.0F)                 pos -= 1.0F;             image.uvRect = new Rect(pos, 0, 1, 1);        }     } Сохраняем скрипт и добавляем его, как компонент к каждому слою. Скорости у меня такие: Back 0.0001 Middle 0.00015 Top 0.00024 Наслаждаемся успешной работой Если все было сделано правильно, мы получим умопомрачительный эффект, от которого просто невозможно оторвать глаз. У вас остались вопросы или возникли трудности? Пишите в комментариях.
Просте та швидке складання frontend проекту за допомогою gulp

Автор: Дмитро Івченко

Введение В этой статье я детально опишу процесс сборки FrontEnd проекта, который стал незаменимым в моей ежедневной работе, очень облегчил рутинную работу и предоставил больше свободного времени для других задач.  Сейчас в интернете существует большое число различных сборщиков и подходов к ней, и каждый выбирает для себя что-то по вкусу. Я только опишу вам один из многих подходов, а вы уже решите сами, подходит он вам или нет. Как уже все догадались, речь пойдет о сборщике под названием Gulp. Скажу сразу, что у вас  должен быть установлен Node.js. У кого не установлен, то вам сюда https://nodejs.org/en/download/ Итак, приступим: Откроем консоль и создадим папку mkdir nebo15 Зайдем в папку, которую только что создали cd nebo15/ запустим команду инициализации проекта: npm init Заполняйте поля теми данными, которые вы знаете, остальные просто пропускайте, нажав Enter. На выходе получим что-то наподобие этого: {    "name": "nebo15",   "version": "1.0.1",   "description": "first gulp project",   "author": "stranger",   "license": "MIT",   "main": "main.js",    "dependencies": {       "gulp": "^3.9.0"    } } В строке dependencies я указал, что нам понадобится gulp и тут же будут появляться все наши плагины. Основные плагины, с которыми я хочу вас познакомить: gulp-minify-css — нужен для минимизации CSS кода, создаст один файл *.min.css gulp-uglify — будет минимизировать наш JS, создаст один файл *.min.js gulp-autoprefixer — авто-добавление добавляет вендорные префиксы (-webkit, -o, -moz) к CSS свойствам, нужно чтобы ваш код поддерживался во всех браузерах. browser-sync — понадобится  для запуска локального сервера. gulp-sass — предназначен для компиляции SCSS и SASS кода. gulp-sourcemaps — предназначен для генерации css sourscemaps, которые понадобятся при отладке кода //= footer.html   эти комментарии будут заменены на код с файла при компиляции gulp-watch — предназначен для отслеживания за изменениями файлов. rimraf —  для очистки папок по завершению сборки (rm -rf для nodejs). После установки всех плагинов у нас будет файл package.json: делается это при помощи команды npm install name_of_package --save-dev  где --save-dev означает то, что пакет будет остановлен в проект, а не глобально. Для глобальной установки используйте тег -g. Bower Сейчас просто не представляю своей жизни без установщика Bower и хочется верить, что вы не исключение. Если же нет, то почитать о Bower можно тут. Давайте поставим его в наш проект. Для установки напишем в консоли: bower init Заполняйте поля те, что знаете, остальное пропускайте. В конце вы получите файл bower.json: {   "name": "nebo15",   "version": "1.0.0",   "authors": [     "Dima"   ],   "license": "stranger",   "ignore": [     "**/.*",     "node_modules",     "bower_components",   ], "dependencies": {     "normalize.css": "*",     "jquery": "2.*" } Установим пакеты с dependencies: bower i Давайте создадим базовую структуру и настроим сборку нашего проекта.   Структура проекта: Сначала нужно 2 папки. Первая (src), в которой мы собственно будем писать код, и вторая (build), в которую сборщик будет собирать файлы.  Текущая структура у нас выглядит так: В src создали типичную структуру для проекта. Сделаем main файлы в папках js/ и style/ и создадим стартовую index.html страничку такого содержания. index.html Структура папки src теперь будет выглядеть так: Тут все просто: fonts —папка с шрифтами img — папка с картинками js — папка со скриптами. Тут в корне - main.js, а рабочие файлы в папке partials style — папка со стилями. Тут в корне - main.scss, а рабочие файлы в папке partials template — тут будем хранить  html код Все html страницы, которые мы верстаем, будут лежать в папке src Gulpfile.js Итак, начнем, сначала подключим все зависимости в этот файл. gulpfile.js Так же создадим js объект, в который пропишем все нужные нам пути, чтобы при необходимости легко в одном месте их редактировать: Веб сервер Чтобы иметь livereload, нам необходимо написать себе веб-сервер. Для этого напишем следующую таску : Сборщик для html Напишем таску для сборки верстки html Стоить вспомнить, что rigger - плагин, позволяющий использовать конструкцию для подключения файлов: //= template/footer.html В папке src/template/  создадим файлы header.html и footaer.html В папку header.html поместим В папку footer.html поместим а файл index.html отредактируем соответственно так: Запускаем сборку следующей командой gulp html:build После того, как она соберется, переходим в папку build и увидим там наш файл index.htm l Сборщик для JS Таска для сборки скриптов будет примерно такая Зададим структуру для main.js: Запустим сборку js: gulp js:build  И в папке build/js  можно увидеть скомпилированный и модифицированный файл. Сборщик для стилей Таска для сборки SCSS: Здесь все легко, но если вы заинтересовались настройками автопрификсера, то об этом можно почитать тут. Запустим нашу таску gulp style:build Теперь давайте определим таску с именем «build», которая будет запускать все: Отлеживаем изменения в файлах Чтобы не запускать каждый раз сборщик вручную, давайте попросим gulp при изменении какого-то файла  перезапускаться.  Понять это просто. Просто при изменениях какого-то файла происходит пересборка проекта. Попробуйте запустить в консоли: gulp watch Измените что-то в проекте и увидите, что произойдет. Очистка Если вы прикрепите большую библиотеку, потом запустите задачу js:build и потом решите, что она вам больше не нужна и удалите ее, то она все равно останется в папке проекта build. Так что было бы удобно периодически удалять ее содержимое. Создадим для этих потребностей простую таску Теперь, запуская команду с консоли gulp clean папка build будет удаляться. Напоследок Мы должны определить стандартную таску, которая запустит всю нашу сборку , используя только одну команду. gulp.task('default', ['build', 'webserver', 'watch']); Запустим в консоли gulp Вот и все. Теперь вы можете настроить проект для себя. Надеюсь, данная статья оказалась познавательной для вас.
Система контролю версій "просто, як двері" або як навчитися користуватися git за пару годин

Автор: Дмитро Івченко

Введение Я достаточно давно использую git практически во всех проектах. За это время я успел многому научиться и хочу поделиться опытом с читателями. Как известно, все новое пугает. Так говорит половина моих знакомых, которые не захотели потратить пару часов, чтобы разобраться с git раз и навсегда. Скорее всего, после прочтения этой статьи и они, и вы поймете, что это проще, чем кажется. Я постараюсь показать, как эта VCS (Version Controll System) помогает разрабатывать проект. Эта статья рекомендована к прочтению тем, кто только делает первые шаги в разработке больших проектов. Итак, Git — это «распределенная система контроля версий», где «система контроля версий» означает то, что она запоминает историю изменения своих файлов, а распределенная значит то, что мы можем работать с ней без доступа к интернету. Для того чтобы начать: Git акаунт на https://github.com Консоль или же графический интерфейс. Человек по ту сторону монитора, что сможет это поставить под свою ось, будь то Windows, Mac OS или Linux . Моё окружение - это Mac OS X + Webstorm + Git + Bash . Если у вас Windows, то, скорее всего, будет msysgit (git-bash) + TortoiseGit. Как вы уже поняли, придется работать с консолью, потому вам будет необходимо усвоить несколько команд для установки git на вашу операционную систему. Поговорим немного о том, как же установить git: Установка для Linux Установить Git под Linux как бинарный пакет можно, используя обычный менеджер. Если у вас установлена Fedora, можно воспользовать yum: $ yum install git-core Если же у вас дистрибутив, например, Ubuntu,  то вам нужно: $ apt-get install git Установка для Mac Существует два способа установки Git на Mac OS X. Первый - использовать графический компоновщик Git, который вы можете скачать здесь: http://goo.gl/nB31LK или же, для более продвинутых пользователей, просто вставьте следующую строчку в терминал $ sudo port install gitweb + git-core +doc  + bash_completion Установка в Windows Настроить Git в Windows просто: у проекта msysGit процедура установки  самая легкая. Просто скачайте *.exe инсталлятор на GitHub: http://goo.gl/cmGQSj После установки у вас будет и консольная версия, и стандартная графическая. Остались вопросы по установке? Тогда вам сюда - https://goo.gl/lec2M0 Git — это коллекция инструментов для решения определенных проблем. Давайте рассмотрим компоненты Git пошагово: Коммиты Коммит (патч) — показывает некоторые изменения в файлах, что подверглись изменениям с момента предыдущего коммита. Коммит также содержит заголовки, в которых есть автор, время его создания и прочее. Git-коммит шифрует это в заголовке «parent», указывая, после какого коммита его нужно наложить. Как применить коммит к другому коммиту? Мы можем применить коммит только к полному набору файлов. Но после этого мы получим измененный набор файлов. Поэтому «коммит» также показывает состояние репозитория после добавления нового патча. История Git — это вся цепочка изменений для воссоздания кодовой базы с нуля, коммит за коммитом. История коммиттов может выглядеть так: Commit C: Родитель — B. Добавь “Systematics” в конец файла «readMe.txt». Commit B: Родитель — A. Добавь “Bionic” в конец файла «readMe.txt». Commit A: Создай файл «readMe.txt», содержащий “Cyber”. Commit A здесь первый, это значит, что у него нет родителя. Это значит, что его патч может только добавить новые файлы — в репозитории нет никаких существующих файлов для изменения. Во всем остальном это такой же коммит, как и все остальные. Итак, мы начинаем с пустого файла. Затем мы применяем патч A, который дает текст “Cyber”. Затем мы можем применить патч B, который даст нам “Cyber Bionic”.  Наконец, мы применяем C, который дает “Cyber Bionic Systematics”. Визуализация Git изображает историю коммиттов слева-направо, описанное выше будет выглядеть так: A → B → C. Тэги Тэги —  имена для коммитов, чем-то похожие на ветки. Однако, тэги должны быть постоянными: они, в основном, используются для названий версий релизов. Вы можете переключаться по тэгу, но тэг не может быть вашей «текущей веткой», и тэг никогда не появится автоматически, если вы коммитите впервые. Также тэги (чаще всего) глобальные, с неограниченным пространством имен, как ветки. Как просмотреть историю последних коммитов: git log покажет название последних трех коммитов в ветке. git log --oneline --graph --decorate намного лучше для просмотра (можете сами убедиться). Вы также можете установить tig, который делает, в основном, то же самое, но вы сможете использовать Enter на коммите, чтобы увидеть различия. git log --follow показывает нам все изменения, только для конкретного файла (директории). --follow означает - следить за историей файла, включая изменение имени файла. А сейчас время для команд, которые вы будете писать каждый день по многу раз $ git status покажет, в какой ветке вы находитесь и какие файлы подверглись изменениям. git add file_name добавляет только указанный файл (файлы) к патчу. git add -A (all) добавляет к патчу все изменения, которые нужны вам в последующем коммите. git commit -a откроет редактор для ввода текста коммита, затем будет создан коммит со всеми произведенными изменениями. git commit -m “what is to change” запишет коммит и даст ему то описание, которое есть в скобках git branch name создает новую ветку под названием name, основаную на коммите, но не переключается на нее. Для этого тебе понадобиться команда: git checkout -b origin/develop, которая создаст новую ветку, основанную на origin/develop, или на той, в которой вы находитесь, и переключится на нее. git checkout производит переключение в ветку, которую вы укажите. Вы также можете перейти в удаленную ветку, в тэг или в конкретный коммит.   git rm file_name говорит Git, что ты собираешься удалить файл, а также удаляет его физически. Конфликты слияния Если вы делаете слияние или отправку изменений, возможно, ваши изменения будут конфликтовать с чужими. Git выведет сообщение: "Автоматическое слияние не удалось, вы должны сделать это вручную". Если вы посмотрите git status, то обнаружите новую секцию для конфликтующих файлов. И вам нужно это исправить, чтобы завершить слияние. Откройте конфликтующий файл и увидите что-то вроде этого: <<<<<<< то что мы изменили ======== то что сделал ваш предшественник >>>>>>>> origin/develop   Это говорит нам о том, что двое людей отредактировали одни и те же строки в том же самом файле по-разному, а Git не знает, что должно быть результатом. Просто отредактируйте файл, как нужно, и выполните git add -А, чтобы сообщить Git, что вы готовы к отправке. Когда все конфликты будут решены и все файлы добавлены через git add -A, сделайте простой git commit для полного слияния. Примечание: Проверяйте, что вы действительно исправили все конфликты в файлах после слияния. Иногда, конфликт - это когда один программист отредактировал файл, а другой удалил этот файл. Когда такое случается, Git покажет тебе, кто что сделал. Надеюсь, что статья дала вам много новых знаний. И если это так - то вперед, создавать первый репозиторий на github.com и закреплять все, что прочитали.
Валідація AngularJS

Автор: Редакция ITVDN

Введение Валидация достаточно часто вызывает затруднения с работой в веб-приложениях. Во многих случаях фреймворки должны быть использованы для валидации значений формы. Кроме того, эти фреймворки часто не работают во всех браузерах. AngularJS приходит с проверкой, построенной так, что теперь гораздо легче создать валидацию, которая работает во всех браузерах. На практике Использование angular-message Чтобы использовать angular-message, нужно внести в ваш проект модуль:  angular.module("realestateApp", ["ngMessages"]); Теперь ng-message будет доступен для использования. Формы Для инициализации процесса валидации, Вы должны начать с формы контейнера: <form name="tenantForm"> Теперь внутри тега можно добавить управление и логику на их проверку. В сценариях валидации, как правило, есть несколько основных атрибутов. Те, которые стоит использовать: Required, Minimum, Maximum, Pattern, Email, Number, и URL. Required Этот атрибут заставляет form быть недействительным, если обязательное поле не вводится. <input type="text" required /> Minimum Length Этот атрибут указывает минимум символов для ввода до того, как значение будет принято. <input type="text" ng-minlength=5 /> Maximum Length Этот атрибут указывает максимальную длину или проверка будет неверна. <input type="text" ng-maxlength=20 /> Pattern Matching Эта функция позволяет согласовать совпадения при использовании Regex. <input type="text" ng-pattern="[a-zA-Z]" /> Email Matching Angular обеспечивает пользовательские функции по электронной почте. <input type="email" name="email" ng-model="user.email" /> Number Этот требует ввода в цифровом формате перед проверкой. <input type="number" name="age" ng-model="user.age" /> URL Этот требует ввода в ссылочном формате перед проверкой. <input type="url" name="homepage" ng-model="user.url" /> Сообщение об ошибке Ранее использовался error-container, но теперь можно использовать директиву ng-message: <div ng-messages="tenantForm.Email.$error" ng-messages-include="messages.html" class="errors"></div> Также нужен файл messages.html для хранения сообщений об ошибках: <div class="messages"> <div ng-message="required">Required</div> <div ng-message="minlength">Too short</div> <div ng-message="maxlength">Too long</div> <div ng-message="email">Invalid email address</div> <div ng-message="compareTo">Must match the previous entry</div> <div ng-message="number">Must be a number</div> <div ng-message="url">Must be in URL format</div> </div> Сообщение об отмене ошибки Иногда Вы должны иметь пользовательские сообщения об ошибках, не охватываемых messages.html. Вы можете сделать это, добавив тег span в диапазон для любого сообщения об ошибке. То, что должно отобразиться: <div ng-messages="tenantForm.FirstName.$error" ng-messages-include="messages.html" class="errors"> <span class="messages" ng-message="minlength">Must be more than 3 characters</span> <span class="messages" ng-message="maxlength">Must be more than 20 characters</span> </div> Собираем всё вместе Теперь, объединив messages.html с index.html. <form name="tenantForm" novalidate style="width: 500px">     <div class="row">         <div ng-repeat="tenant in tenant">             <div class="form-group">                 <label>First Name: label>                 <input type="text"                        placeholder="First Name"                        name="FirstName"                        ng-model="tenant.FirstName"                        ng-minlength=3                        ng-maxlength=20 required />                 <div ng-messages="tenantForm.FirstName.$error"                      ng-messages-include="messages.html" class="errors">                     <span class="messages"                     ng-message="minlength">Must be more than 3 charactersspan>                     <span class="messages"                     ng-message="maxlength">Must be more than 20 charactersspan>                 div>             div>             <div class="form-group">                 <label>Home Phone: label>                 <input type="number"                        placeholder="Phone Number"                        name="HomePhone"                        ng-model="tenant.HomePhone"                        ng-minlength=7                        ng-maxlength=10 required />                 <div ng-messages="tenantForm.HomePhone.$error"                      ng-messages-include="messages.html" class="errors">                     <span class="messages"                     ng-message="minlength">Must be more than 7 digitsspan>                     <span class="messages"                     ng-message="maxlength">Must be less than 11 digitsspan>                  div>             div>             <div class="form-group">                 <label>Email: label>                 <input type="email"                        placeholder="Email"                        name="Email"                        ng-model="tenant.Email"                        required />                 <div ng-messages="tenantForm.Email.$error"                      ng-messages-include="messages.html" class="errors">div>             div>             <div class="form-group">                 <label>Webpage: label>                 <input type="url"                        placeholder="Webpage"                        name="Webpage"                        ng-model="tenant.Webpage"                        required />                 <div ng-messages="tenantForm.Webpage.$error"                      ng-messages-include="messages.html" class="errors">                 div>             div>         div>     div>               form> Этот подход кажется чище, чем использование директивы ng-show. Кроме того, это уменьшит дублирование кода путем центрального места хранения ваших сообщений об ошибках, но в то же время используется для пользовательских сообщений. Источник: http://www.codeproject.com/Articles/992545/AngularJS-Validation
Як створити веб-сайт за допомогою AJAX

Автор: Редакция ITVDN

Создания простого чата Для начала создадим простой чат с помощью HTML&CSS и PHP&MySQL Проектирование Базы Данных Переходим к phpMyAdmin. Создаем новую базу данных, называем “chatdb”. Создаем новую таблицу, называя “Posts”, включая в себя 4 столбца: “id” тип колонки INT, автоматическое увеличение на 1 (задаем A_I в ячейку с флажком) основной ключ (index); “nick” тип колонки VARCHAR и длина 100; “post_text” тип колонки TEXT; “post_dt” тип колонки DATETIME по умолчанию является CURRENT_TIMESTAMP. Создание веб-сайта. Во-первых, создадим главную страницу. Список комментариев и отправка формы, используя кнопку. Всё будет находится в файле “index.php” .  Давайте создадим этот файл и напишем что-то вроде этого:  <html> <head> head> <body> php $mysqli = new mysqli("127.0.0.1", "root", "", "chatdb"); /* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */ /* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */ /* "chatdb" is DB's name. */ /* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */ $result = $mysqli->query("SELECT * FROM posts;"); ?> <style> /* All CSS is very simplified. I provide it for example and no more.  * In Chrome it works tolerably, but in IE and Firefox it works very poorly.  */ .content {   display: table;   width: 50%;   min-width: 400px;   height: 80%;   /* Center horizontally and vertically */   position: absolute;   left: 0; right: 0;   top: 0; bottom: 0;   margin: auto;   /* Design */   border: 1px solid;   background-color: silver;   padding: 5px; } /* For mobile devices */ @media (max-width: 400px) {   .content {     width: 100%;     min-width: 0;     padding: 0px;   } } style> <div class="content"> <div id="comments" style="overflow-y: scroll; height: 100%;"> php if ($result) {   while ($post = $result->fetch_object()){     $nick = $post->nick;     $post_dt = $post->post_dt;     $post_text = $post->post_text;         echo "<b>$nickb> ($post_dt):<br>";     echo "$post_text<br>";     echo "<br>";   }   $result->close(); } ?> div> php $mysqli->close(); ?> <form action="post.php" method="post" style="height: 0; display: table-row;">   Nick:<br>   <input type="text" name="nick" style="width: 100%;">input><br>   <br>   Text:<br>   <textarea name="text" style="width: 100%;">textarea><br>   <br>   <input type="submit">input> form> div> <script type="text/javascript">     var divComments = document.getElementById('comments');     divComments.scrollTop = divComments.scrollHeight; script> body> html> 2. Дальше давайте создадим “post.php” файл и напишем такое:  php $nick = $_POST['nick']; $post_text = $_POST['text']; $mysqli = new mysqli("127.0.0.1", "root", "", "chatdb"); $nick = $mysqli->real_escape_string($nick); $nick = htmlspecialchars($nick); $post_text = $mysqli->real_escape_string($post_text); $post_text = htmlspecialchars($post_text); $mysqli->query("INSERT INTO posts (nick, post_text) VALUES ('$nick', '$post_text');"); $mysqli->close(); /* Redirect To Main Page */ header('Location: ' . $_SERVER['HTTP_REFERER']); ?> 3. Откроем ваш чат в любом продвинутом браузере. Вся работа:  4. Напишите ник и текст, а потом нажмите на кнопку отправки.  Когда отправили форму, сразу же происходит перенаправление формы главной страницы в “post.php”. “Post.php” моментально наполняет данными таблицу и перенаправляет на главную страницу. Также “post.php” содержит начальный XSS и SQL защищенный вход. Для упрощения не отправляем клиентам время с JS на “post_dt” на внесения данных, а указываем значение по умолчанию – CURRENT_TIMESTAMP, который предоставляет нынешнюю дату и время на сервер. Когда количество комментариев больше, чем экран может вместить, то комментарии   переполнены в div, используется вертикальный скроллбар. Во время загрузки страницы JS автоматически опускает скроллбар вниз к недавнему комментарию. Что тут не так? Проблема номер 1. Новый непрочитанный комментарий от пользователя не загружается в базу данных автоматически, без ручной перезагрузки страницы. Это очень, очень серьезная проблема для любого чата. Как это исправить? Очевидно, один из путей сделать это - использовать HTTP-запрос к фоновой работе автоматически (с помощью JS) и асинхронно, то есть это один из способов обеспечить выполнение AJAX в любой form. Проблема номер 2. Также неправильно то, что кнопка отправки перезагружает страницу (перенаправляет на вторую страницу и следующим шагом возвращает назад). При перезагрузке страницы сбрасывает “Nick” в  поле (также, как любые другие изменения, те, что сделали с пользователем) Как это исправить? Конечно, можно обеспечить выполнение обхода для сохранения изменений и перезагрузки на перезагружаемой странице. Но не эффективнее ли устранить причину, чем последствие? Можно только убрать перезагружаемую страницу и эта проблема будет решена автоматически. Проблема номер 2.1. Видите ли что-то необычное тут? Это слишком быстро? Дело с чатом очень простое – загружаем только два маленьких текстовых параметра. Что делать, если нужно улучшить чат с помощью добавления присоединения, а конкретно - изображений и видео? Видео может иметь объем в размере нескольких мегабайт, что тогда в этом случае? Давайте попробуем.  Давайте немного изменим “post.php”, после $mysql->query() добавив это:  for ($i = 0; $i < 1000000000; $i++) { } Старайтесь размещать какие-либо комментарии. Что мы видим? Нет, UI не остановилось (заморозилось), но браузер ждет до того, как закончится подключение:   Да, если “post.php” вызывает какую-то необработанную ошибку, то чат исчезает, и пользователь видит пустое окно с сообщением о непонятной ошибке. Чтобы вернуться к чату, пользователю стоит нажать кнопку “Back” в браузере. И это проблема номер 2.2. Давайте уберем петлю с “post.php” и исправим эти проблемы. Реализация легкого AJAX в простой чат Автоматическое обновление комментариев Эта веб-страница без AJAX? Это веб-страница, которая полностью перезагружается. Эта веб-страница c AJAX? Это веб-страница, что перезагружается частично. Где же взять части этих страниц? Стоит разделить нашу страницу на части, в этом случае сервер генерирует страницу частично. Страница будет состоять из двух частей – блок комментариев и другой контент страницы. Комментарии будут загружаться и перезагружаться отдельно от другой страницы. Давайте сделаем первую часть, то есть блок комментариев. Для начала создадим пустой файл, назовем “getcomment.php”. Дальше переходим в “index.php” для того, чтобы вырезать комментарии и далее отделить его. 1. Вырезаем инициализированный блок MySQL с “index.php"  php $mysqli = new mysqli("127.0.0.1", "root", "", "chatdb"); /* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */ /* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */ /* "chatdb" is DB's name. */ /* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */ $result = $mysqli->query("SELECT * FROM posts;"); ?> вставляем в “getcomments.php”. 2. Следующее, вырежем контент div c комментариями с “index.php”: php if ($result) {   while ($post = $result->fetch_object()){     $nick = $post->nick;     $post_dt = $post->post_dt;     $post_text = $post->post_text;         echo "<b>$nickb> ($post_dt):<br>";     echo "$post_text<br>";     echo "<br>";   }   $result->close(); } ?> И вставим (добавим) в “getcomments.php” после инициализации MySQL. 3. Дальше, вырежем недалекий блок MySQL с “index.php”: php $mysqli->close(); ?> Вставим его в конец файла “getcomments.php”. 4. Наконец-то, убираем JS-скрипт, который опускает скролл в самый низ. <script type="text/javascript"> var comments = document.getElementById('comments'); comments.scrollTop = comments.scrollHeight; script> Не переживай, это только временно. 5. Сделано. Теперь имеем что-то наподобие этого:  <html> <head> head> <body> <style> /* All CSS is very simplified. I provide it for example and no more.  * In Chrome it works tolerably, but in IE and Firefox it works very poorly.  */ .content {   display: table;   width: 50%;   min-width: 400px;   height: 80%;   /* Center horizontally and vertically */   position: absolute;   left: 0; right: 0;   top: 0; bottom: 0;   margin: auto;   /* Design */   border: 1px solid;   background-color: silver;   padding: 5px; } /* For mobile devices */ @media (max-width: 400px) {   .content {     width: 100%;     min-width: 0;     padding: 0px;   } } style> <div class="content"> <div id="comments" style="overflow-y: scroll; height: 100%;"> div> <form action="post.php" method="post" style="height: 0; display: table-row;">   Nick:<br>   <input type="text" name="nick" style="width: 100%;">input><br>   <br>   Text:<br>   <textarea name="text" style="width: 100%;">textarea><br>   <br>   <input value="Submit" type="submit">input> form> div> body> html> getcomments.php:  php $mysqli = new mysqli("127.0.0.1", "root", "", "chatdb"); /* "127.0.0.1" is MySQL host name. In local servers (XAMPP, Ampps, etc.) it is 127.0.0.1. If you using a dedicated hosting, see it in admin panel. */ /* "root" and "" is login and password for DB's user. In local servers usually default DB user is "root" with empty password. */ /* "chatdb" is DB's name. */ /* Warning: in XAMPP you should manually run MySQL server (from xampp-control.exe) to get it work. */ $result = $mysqli->query("SELECT * FROM posts;"); ?> Вероятно, теперь наша страница разделена. Давайте проверим её. Переходим http (точнее ССЫЛКА для локального сервера). Дальше, переходим на главную страницу, http:///index.php Это удивительно! Всё удачно получилось, разделив страницу на части за пару минут! Не останавливаемся на этом. Теперь “glue” эти части с помощью AJAX. Как это сделать? 1. Для начала стоит создать пустой JS-скрипт на главной странице: <script type="text/javascript"> alert('Test'); script> Добавив это после тега div, перед закрывающимся тегом . 2. Сделаем HTTP GET запрос от JS к “getcommet.php” Для этого используем XMLHtttpRequest (XHR) класс: <script type="text/javascript"> var xhr = new XMLHttpRequest(); xhr.open('GET', '/getcomments.php', false); xhr.send(null); if (xhr.status == 200) {   alert(xhr.responseText); } script> Это работает, но не читает старые версии IE те, что не поддерживают такую инициализацию. Для получения более кросс-браузерного пути переходим в и добавляем это: <script type="text/javascript"> function getXmlHttp(){   var xmlhttp;   try {     xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");   } catch (e) {     try {       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");     } catch (E) {       xmlhttp = false;     }   }   if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {     xmlhttp = new XMLHttpRequest();   }   return xmlhttp; }; script> И главный скрипт (в конце) <script type="text/javascript"> var xhr = getXmlHttp(); xhr.open('GET', '/getcomments.php', false); xhr.send(null); if (xhr.status == 200) {   alert(xhr.responseText); } script> Как видно, теперь JS получает контент со страницы “getcomments.php” и показывает это в предупреждении.   2.1. Вопрос: «Это на самом деле AJAX (Asynchronous Javascript And Xml)?» Это AJAX, потому что запрос сервера отформатирован в HTML (который основан на XML). Но действительно ли это AJAX, это асинхронно? Проверим. Добавим эти уже знакомые строки в любое место между в “getcomments.php”: for ($i = 0; $i < 1000000000; $i++) { } Что теперь видно на загружаемой странице? Вначале страница зависает, его замораживает UI (становится не реагирующим на нажатие левой/правой кнопки мыши): Дальше Chrome показывает навязчивое всплывающие окно, сообщающее об удалении страницы: Это не AJAX! Это JAX! Как его сделать асинхронным? К счастью, ХHR также поддерживает асинхронный режим: <script type="text/javascript"> var xhr = getXmlHttp(); xhr.open('GET', '/getcomments.php', true); /* true for asynchronous */ xhr.onreadystatechange = function() {   if (xhr.readyState == 4) {     if(xhr.status == 200) {       alert(xhr.responseText);     }   } }; xhr.send(null); script> В этом случае браузер не ждет ответа с главного потока пользовательского интерфейса, он запускает в другом (асинхронно) и вызываемым событием “onreadystatechange” в главном контексте UI.  Теперь всё в порядке, страница полностью доступна, пока запрос запущен, и после ответа получит предупреждение. for ($i = 0; $i < 1000000000; $i++) { } И продолжаем работу. 3. Добавляем этот контент в div  вместо предупреждения. Заменить это: alert(xhr.responseText);  C этим: var divComments = document.getElementById('comments'); divComments.innerHTML = xhr.responseText; Возвращаем назад, клиент видит “glued” страницу с блоком комментариев. 4. Дальше следует установить интервал для автоматической проверки новых комментариев время от времени… и также восстановить удаленный сценарий автоматической прокрутки.  <html> <head> <script type="text/javascript">     function getXmlHttp() {         var xmlhttp;         try {             xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");         } catch (e) {             try {                 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");             } catch (E) {                 xmlhttp = false;             }         }         if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {             xmlhttp = new XMLHttpRequest();         }         return xmlhttp;     }; script> head> <body> <style> /* All CSS is very simplified. I provide it for example and no more.  * In Chrome it works tolerably, but in IE and Firefox it works very poorly.  */ .content {   display: table;   width: 50%;   min-width: 400px;   height: 80%;   /* Center horizontally and vertically */   position: absolute;   left: 0; right: 0;   top: 0; bottom: 0;   margin: auto;   /* Design */   border: 1px solid;   background-color: silver;   padding: 5px; } /* For mobile devices */ @media (max-width: 400px) {   .content {     width: 100%;     min-width: 0;     padding: 0px;   } } style> <div class="content"> <div id="comments" style="overflow-y: scroll; height: 100%;"> div> <form action="post.php" method="post" style="height: 0; display: table-row;">   Nick:<br>   <input type="text" name="nick" style="width: 100%;">input><br>   <br>   Text:<br>   <textarea name="text" style="width: 100%;">textarea><br>   <br>   <input value="Submit" type="submit">input> form> div> <script type="text/javascript">     var divComments = document.getElementById('comments');     function loadComments() {         var xhr = getXmlHttp();         xhr.open('GET', '/getcomments.php', true);         xhr.onreadystatechange = function () {             if (xhr.readyState == 4) {                 if (xhr.status == 200) {                     if (xhr.responseText !== divComments.innerHTML) {                         divComments.innerHTML = xhr.responseText;                         divComments.scrollTop = divComments.scrollHeight;                     }                 }             }         };         xhr.send(null);     };     loadComments();     setInterval(loadComments, 1000) script> body> html> Теперь проблема исправить это. Новые комментарии с другого пользовательского чата (другие вкладки браузеров, окон и экземпляров) получают каждые 1000 миллисекунды (1 секунду) автоматически. Но отправка комментариев уже вызывает перезагрузку нашей страницы. Отправка комментариев без перезагрузки Как написано выше, XHR помогает отправлять HTTP GET-запросы без перезагрузки страницы и GUI заморозки (асинхронно). Теперь отправляем HTTP-запрос асинхронно, но на этот раз POST запрос, а не GET. И, естественно, XHR позволяет это. Используем метод send(). Для GET указываем null. Для POST устанавливаем запрос “body”.Также нужно добавить “Content-Type:application/x-www-from-urlencoded” в header  для того, чтобы разрешить серверу знать, какой формат использовать для отправки данных. Заметка: если не знаете, что отправлять, то можно захватить регулярный запрос с помощью “Fiddler” или же любой другой HTTP-перехватчик и только просимулировать запрос. HTTP-перехватчик — это незаменимый инструмент для работы с HTTP/HTTPS. Это позволяет увидеть все headers и bodies по всем HTTP(S)-запросам, что отправляются в систему. Лучше использовать “Fiddler”, это бесплатное, современное и очень простое приложение, что может поддерживать HTTP/HTTPS и оба Win x86/x64. 1. Для начала создадим пустой JS скрипт в HTML. Разместить до самого тега, потому что этот скрипт будет использован для отправки формы комментария (форма будет вызывать этот скрипт при отправке).2. В этом скрипте, реализуем функцию, что будет отправлять ник и комментарии в “post.php”  <script type="text/javascript"> function postComment(nick, text) {   var xhr = getXmlHttp();   xhr.open('POST', '/post.php', true);   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');   xhr.onreadystatechange = function() {     if (xhr.readyState == 4) {       if (xhr.status == 200) {         /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */       }     }   };   xhr.send('nick=' + nick + '&text=' + text); /* joining the data in format simulates form */ }; script> 3. Следующие, перезагружая страницу при отправке:  ... onsubmit="return false;"> Также добавим вызов “postComment’s”: action="post.php" method="post" style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;"> 4. Наконец, необязательно, но можно убрать “action” и “method” с формы:  <form style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;"> 5. Результат: <html> <head> <script type="text/javascript">     function getXmlHttp() {         var xmlhttp;         try {             xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");         } catch (e) {             try {                 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");             } catch (E) {                 xmlhttp = false;             }         }         if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {             xmlhttp = new XMLHttpRequest();         }         return xmlhttp;     }; script> head> <body> <style> /* All CSS is very simplified. I provide it for example and no more.  * In Chrome it works tolerably, but in IE and Firefox it works very poorly.  */ .content {   display: table;   width: 50%;   min-width: 400px;   height: 80%;   /* Center horizontally and vertically */   position: absolute;   left: 0; right: 0;   top: 0; bottom: 0;   margin: auto;   /* Design */   border: 1px solid;   background-color: silver;   padding: 5px; } /* For mobile devices */ @media (max-width: 400px) {   .content {     width: 100%;     min-width: 0;     padding: 0px;   } } style> <div class="content"> <div id="comments" style="overflow-y: scroll; height: 100%;"> div> <script type="text/javascript">     function postComment(nick, text) {         var xhr = getXmlHttp();         xhr.open('POST', '/post.php', true);         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');         xhr.onreadystatechange = function () {             if (xhr.readyState == 4) {                 if (xhr.status == 200) {                     /* it isn't required to add comment to DOM manually, it will done automatically on next refresh via AJAX */                 }             }         };         xhr.send('nick=' + nick + '&text=' + text);     }; script> <form style="height: 0; display: table-row;" onsubmit="postComment(this.nick.value, this.text.value); return false;">   Nick:<br>   <input type="text" name="nick" style="width: 100%;">input><br>   <br>   Text:<br>   <textarea name="text" style="width: 100%;">textarea><br>   <br>   <input value="Submit" type="submit">input> form> div> <script type="text/javascript">     var divComments = document.getElementById('comments');     function loadComments() {         var xhr = getXmlHttp();         xhr.open('GET', '/getcomments.php', true);         xhr.onreadystatechange = function () {             if (xhr.readyState == 4) {                 if (xhr.status == 200) {                     if (xhr.responseText !== divComments.innerHTML) {                         divComments.innerHTML = xhr.responseText;                         divComments.scrollTop = divComments.scrollHeight;                     }                 }             }         };         xhr.send(null);     };     loadComments();     setInterval(loadComments, 1000) script> body> html>  Теперь простая реализация AJAX подошла к концу. Это ещё не конец, это только начало. Есть ещё достаточно всего для изучения и улучшения.
Flappy Bird за 30 хвилин

Автор: Олег Загородній

Введение Не так давно была популярной такая мобильная игра, как Flappy Bird.  Причем по разным причинам из магазинов она пропала также неожиданно, как и появилась. Однако, учитывая ее популярность, сразу образовалось множество клонов. Не столь качественных, конечно. Но почему их было много? Все из-за того, что сама игра делается довольно просто и быстро. И в этой небольшой статье мы, разумеется, рассмотрим, как же сделать такую игрушку, как Flappy Bird. Все ресурсы (спрайты, шрифты) принадлежат непосредственно их авторам. Ресурсы Все, что нам понадобится из ресурсов игры – это несколько спрайтов и шрифт, как в оригинале. Спрайты найдены на просторах интернета. Шрифт был скачан по ссылке: http://www.dafont.com/04b-19.font?text=Flappy+Number Подготовка игровых объектов Сначала сделаем префаб игрока, то есть птичку. Для этого создаем на сцене пустой объект с именем Bird. Внутрь него помещаем объект-спрайт с именем Body и в свойство Sprite компонента Sprite Renderer помещаем спрайт нашей птички (из папки Sprites). Теперь на саму птичку (объект Bird) прикрепляем компонент Circle Collider 2D и задаем его радиусу значение 0.45. Также необходимо прикрепить компонент Rigidbody 2D. Здесь, пожалуйста, не перепутайте. Нам необходим именно 2D компонент, а не обычный Rigidbody. Ему задаем значение гравитации (Gravity Scale) равным 2.45 и запрещаем передвижение по оси X, чтобы наша птичка неожиданно не улетела куда-то в сторону. Теперь создаем C# скрипт c именем BirdHelper и тоже прикрепляем его к птичке (объект Bird). После всего этого перетягиваем объект Bird из окна Hierarchy в окошко Project, создав таким путем префаб птички. То есть в итоге на префабе Bird должно быть четыре компонента: Transform, Circle Collider 2D, Rigidbody 2D и скрипт Bird Helper. С главным героем пока что покончили. Приступим теперь к единственным препятствиям в игре – трубам. Мы с Вами поступим очень хитро. Так как в процессе игры каждая преграда – это пара труб (одна сверху, другая – снизу), их длину можно было бы регулировать и кодом в момент создания. Ведь если нижняя труба короткая, то верхняя – длинная. Но мы пойдем более простым путем. Наша преграда будет сразу состоять из двух длинных труб, и мы просто-напросто будем их ставить выше или ниже. Как на картинке ниже, где светлая рамка – границы дисплея. Что ж, дабы заделать префаб преграды, создаем на сцене пустой объект с именем Pipes и помещаем внутрь него два объекта-спрайта с именами TopPipe и BottomPipe. В Каждому из них в свойство Sprite компонента Sprite Renderer перетаскиваем спрайт Pipe (из папки Sprites). Объекту BottomPipe ставим положение по оси Y -4.5 (отрицательное). С объектом TopPipe проделываем аналогичные манипуляции, но позиция по оси Y будет 4.5, и еще необходимо повернуть его на 180 градусов вокруг оси Z. Почти готово. Осталось только настроить коллайдеры и прикрепить скрипт. Начнем с коллайдеров. Прикрепим  на объект Pipes компонент Box Collider 2D. А лучше сразу три. Первый настроим таким образом, как на картинке. Просто немного подкорректируем размер и зададим позиции по оси X значение -4.5. Как вы, думаю, уже догадались - это будет коллайдер для нижней трубы. Следующий Box Collider 2D настроим аналогично предыдущему, только позиция по оси X будет 4.5. Теперь последний коллайдер. Он, на самом деле, будет триггером, и с помощью него  мы сможем отследить, когда же игрок преодолел текущее препятствие. Вот такие настройки должны быть у этого коллайдера (уже триггера). И под конец создаем скрипт с именем PipesHelper и прикрепляем его на объект Pipes. Теперь перетягиваем объект Pipes из окна Hierarchy в окно Project, создав таким путем префаб для препятствий. Остался только фон. Создаем пустой объект с именем Background. Помещаем в него два объекта-спрайта с именами Part1 и Part2 и в свойство Sprite компонента Sprite Renderer помещаем спрайт Background. Объекту Part1 задаем размеры X: 2.6, Y: 2.6. С объектом Part2 выполняем такие же действия, но еще сдвигаем его вправо на 7.2 юнитов по оси X. После этого в объект Background помещаем еще два объекта-спрайта с именами Ground1 и Ground2. Им назначаем спрайты Ground из папки Sprites. Вот так должны выглядеть настройки объектов Ground1 и Ground2. Напоследок необходимо добавить коллайдер для земли и создать анимацию движения фона. Прикрепляем компонент Box Collider 2D на объект Background. Для того, чтобы создать анимацию, выделяем Background в окне Hierarchy и в окошке Animation нажимаем кнопку Create. Назовем ее BackgroundFloating. Вся задача данной анимации – передвижение фона влево, чтобы создать эффект, как будто игрок на самом деле летит вправо. После того, как анимация будет создана, на объект Background автоматически прикрепится компонент Animator и будет создан Animator Controller. Нам осталось только перейти в окно Animator и установить значение скорости анимации 0.2. Под конец создаем скрипт GameHelper и цепляем его на игровую камеру. На этом, пожалуй, все игровые приготовления завершены. Непосредственно сам процесс разработки Начнем, я так думаю, из скрипта главного персонажа. То есть BirdHelper’a. Реализация полета птички, как в оригинале, довольно проста. Под действием силы гравитации она будет постоянно падать, а при нажатии клавиши, допустим, Space, мы применим к ней силу по направлению вверх, используя метод AddForce на компоненте Rigidbody2D. using UnityEngine; public class BirdHelper : MonoBehaviour {     public float force;     private new Rigidbody2D rigidbody;     void Awake()     {         rigidbody = GetComponent<Rigidbody2D>();     }     void Update()     {         if (Input.GetKeyDown(KeyCode.Space))             rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);         rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);     } } С помощью метода MoveRotation мы совершаем поворот птицы в зависимости он величины и знака значения текущего ее ускорения. Поле force у нас открытое и, разумеется, отобразится в окне Inspector. Напишем там 8. Это будет сила “прыжка”. Со скриптом главного героя почти всё. Вернемся к нему немножко позже. Перейдем к трубам. Их задача - двигаться на игрока, ведь сам по себе он лишь летает по вертикальной оси в процессе игры и так и не сдвинется по горизонтальной. Движение препятствий можно реализовать статическим методом MoveTowards структуры Vector3. using UnityEngine; public class PipesHelper : MonoBehaviour {     [SerializeField]     private float speed;            void Start()     {         Vector2 position = transform.position;         position.y = Random.Range(-1.5F, 2.5F);         transform.position = position;         Destroy(gameObject, 6.0F);     }        void Update()        {         transform.position = Vector2.MoveTowards(transform.position, transform.position - transform.right, speed * Time.deltaTime);        } } При появлении препятствие будет выбирать случайную позицию по оси Y, но такую, чтобы не было видно конца верхней или нижней трубы. Затем каждый кадр будет двигаться влево. Для этого, задавая конечную точку движения, мы вычитаем из текущей позиции препятствия вектор, направленный вправо от него. Также через 6 секунд объект Pipes будет уничтожен, чтобы не нагружать устройство, так как он свою задачу уже выполнил. К полю speed, представляющему скорость движения препятствия, мы применили атрибут SerializeField, чтобы оно было отображено в Inspector’e, ведь мы его закрыли. Перед тем, как перейти к скрипту GameHelper, добавим на игровую сцену объект-текст с именем ScoreText и настраиваем, как на рисунке ниже. Это будет текст для отображения количества очков игрока. Еще давайте добавим объект-кнопку с именем RestartButton. Она будет появляться, когда игрок проиграет, то есть при столкновении с каким-либо препятствием. В свойство SourceImage компонента Image нашей кнопки перетащите спрайт Button из папки Sprites. Вот настройки кнопки. А вот настройки текста внутри кнопки RestartButton. Непосредственно объект кнопки нужно деактивировать. Иерархия объектов на сцене будет выглядеть следующим образом. Какая же задача скрипта GameHelper? Он будет отвечать за генерацию новых препятствий, подсчет очков и их отображение. А еще за перезапуск уровня. Давайте глянем код. using System.Collections; using UnityEngine; using UnityEngine.UI; public class GameHelper : MonoBehaviour {     [SerializeField]     private Text scoreText;     private GameObject pipes;     public Button restartButton;     [HideInInspector]     public int score;     void Awake()     {         pipes = Resources.Load<GameObject>("Pipes");     }     void Start()     {         StartCoroutine(GeneratePipes());     }     void Update()     {         scoreText.text = "Score: " + score;     }     IEnumerator GeneratePipes()     {         Vector2 position;         while(true)         {             position = transform.position;             position.x += 6.0F;             Instantiate(pipes, position, Quaternion.identity);             yield return new WaitForSeconds(2.0F);         }              }     public void Restart()     { # if UNITY_5_2         Application.LoadLevel(Application.loadedLevel); #endif         Time.timeScale = 1.0F;     } } В первую очередь не забудьте подключить пространства имен UnityEngine.UI для работы с элементами пользовательского интерфейса и System.Collections, ведь там находится необходимый нам интерфейс IEnumerator, который мы будем использовать для карутины. Метод Reset отвечает за перезапуск уровня при нажатии соответствующей кнопки. Значит, настройки компонента Button объекта ResetButton необходимо немножко подправить. Так как сцена у нас не тяжелая, то мы можем позволить себе просто перезагружать ее полностью. Но, так как свойство timeScale статическое, его значение стоит снова установить в 1.0, чтобы при перезагрузке сцены время шло своим чередом. Метод GeneratePipes является карутиной и его задача – бесконечным циклом создавать препятствия с задержкой между каждыми в 2.0 секунды. Запускается карутина методом StartCaroutine при старте игры. А в методе Awake подгружается из папки Resources модель препятствия. В поля scoreText и restartButton, которые будут отображены в окне Inspector, необходимо перетащить соответствующие объекты текста очков и кнопки рестарта игры. Каждый кадр в свойство text объекта scoreText будет передавать текущее количество очков, которое хранится в поле score. Почти закончили. Осталось немного дополнить скрипт BirdHelper. using UnityEngine; public class BirdHelper : MonoBehaviour {     public float force;     private new Rigidbody2D rigidbody;     private GameHelper;     void Awake()     {         rigidbody = GetComponent<Rigidbody2D>();         gameHelper = Camera.main.GetComponent<GameHelper>();     }     void Update()     {         if (Input.GetKeyDown(KeyCode.Space))             rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);         rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);     }     void OnCollisionEnter2D (Collision2D collision)     {         gameHelper.restartButton.gameObject.SetActive(true);         Time.timeScale = 0.0F;     }     void OnTriggerExit2D (Collider2D other)     {         gameHelper.score++;     } } Метод OnTriggerExit2D сработает в момент выхода игрока из триггера, который находится внутри препятствия и добавит нам одно очко. OnCollisionEnter2D будет вызван при столкновении с любым коллайдером, а это значит, что игрок проиграл. Время остановится и активируется кнопка перезапуска игры. На этом, пожалуй, всё. Скачивайте проект, изучайте, дополняйте, переделывайте либо просто удаляйте. Спасибо большое всем за внимание. Удачи в начинаниях и творческих успехов!
Notification success