Swift Storyboards в Xcode 6.3: Часть 1

Если вы нашли опечатку в тексте, выделите ее и нажмите CTRL + ENTER.

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

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

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

Если у вы создаете приложение с большим количеством разных страниц, то сториборды позволяют вам существенно уменьшить количество склеивающего их кода, чтобы обеспечить переход от одной страницы к другой. В стародавние времена разработчикам приходилось создавать отдельные файлы (файлы nib или xib) интерфейса для каждого отдельного view controller(страницы). Но теперь ваши приложения используют единственный сториборд, который включает в себя дизайн всех страниц и который определяет взаимодействие между ними.

Сториборды имеют ряд достоинств:

  • В сториборде очень удобно наблюдать общую картину вашего приложения и взаимосвязи между его страницами. В сториборде можно отслеживать все что угодно, потому что общий дизайн приложения содержится в одном единственном файле, а не распределен между несколькими файлами nib.
  • Сториборды могут описывать переходы между различными окнами. Эти переходы называются "segues", которые создаются путем соединения двух страниц прямо в сторибоде. Благодаря этим segues, вы пишите меньше кода для вашего пользовательского интерфейса.
  • Сториборды облегчают вашу работу с тибличными типами, с ячейками. Вы можете создать ваши таблицы практически полностью из сториборда, а ведь опять таки это именно то, что уменьшает в разы ваш код, который вам бы пришлось написать.
  • Сториборды упрощают работу при использование автопозиционирования. Автопозиционирование - мощная функция, которая позволяет определять математические взаимоотношения между элементами, которые имеют определенные размеры и позиции, и так же упрощает работу по отображению вашего приложения на разных устройствах с разными расширениями экрана.

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

Если вы собираетесь использовать nib файлы, что ж, вперед! Но помните, что вы всегда можете комбинировать nib и storyboard файлы между собой, это не тот случай "либо-либо". В этом туториале, посвященном сторибордам, вы увидите на что способен сториборд. Вы создадите несложное приложение, которое будет отображать список игроков и их рейтинг. В процессе создания этого приложения вы познакомитесь с самыми распространенными задачами, которые могут возникнуть у вас при использовании сторибордов.

Поехали!

Запускаем Xcode и создаем новый проект. Используем в качестве стартового шаблона Single View Application.

Заполните поля формы так:

  1. Product Name: Ratings
  2. Organization name: заполняйте как знаете, но если ничего не сможете придумать, то можете воспользоваться названием iPhoneCoder. ;]
  3. Company Identifier: идентификатор, который вы будете использовать для ваших приложений, обычно ваш домен в перевернутом виде
  4. Language: Swift
  5. Devices: iPhone
  6. Use Core Date: оставьте пустым

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

Новый проект состоит из двух классов, AppDelegate и ViewConstroller, и из звезды сегодняшнего дня Main.storyboard.

Это приложение будет состоять только из интерфейса, так что перед тем, как мы продолжим, давайте отключим опции отображения приложения Landscape Left и Landscape Right, которые вы найдете в Deployment Info > Device Orientation и которые видно на изображении выше.

Давайте посмотрим на наш сториборд. Нажмите на Main.storyboard в навигаторе проекта, чтобы открыть его в интерфейс билдере:

Официальный термин сториборда для view controller является "scene", но вы можете использовать оба этих термина как взаимозаменяемые. Но scene (сцена или кадр) является тем, что отображает view controller в сториборде.

Здесь мы используем единственный view controller, который имеет пустое окно. Указывающая на него стрелка с левой стороны, отображает исходный view controller приложения, которое отображено в сториборде.

Проектирование интерфейса в сториборде происходит путем перетаскивания различных контролсов(элементов из библиотеки) из объектной библиотеки (в правом нижнем углу) на ваш view controller. Как это делается вы увидите далее.

Заметка

Вы заметите, что первоначальная сцена имеет квадратную форму. Эту особенность ввели, начиная с Xcode 6, что позволяет использовать автопозиционирование и классовые размеры (Size Classes) по умолчанию в сториборде и nib файлах. Автопозиционирование и классификация размеров по классам - новые мощные технологии, которые позволяют создавать гибкие пользовательские интерфейсы, которые могут легко меняться, что очень удобно, когда пользователи использую разные устройства для вашего приложения или просто телефоны или планшеты с разными размерами дисплеев. Автопозициониарование было представлено в iOS 6, классовые размеры были представлены только в iOS 8. Об этих возможностях программы можно тоже многое рассказать, и вам бы наверняка хотелось научиться ими пользоваться, но это не входит в рамки этого туториала.

До того, как начать наше исследование возможностей сториборда, давайте отключим эти две опции(Auto Layout и Size Classes):

Когда выскакивает окошко с надписью:

Кликните Disable Size, но только убедитесь, что у вас в графе Keep size class data for: стоит iPhone. После этого ваша сцена будет равна четырех дюймовому дисплею iphone. Для того, чтобы ощутить как работает сториборд, перетащите несколько контролсов из библиотеки объектов, которая находится в нижнем правом углу:

По мере перетаскивания различных контролсов, они должны так же появляться и на схеме документа (Document Outline), которая находится слева:

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

Сториборд показывает содержимое всех ваших view controller'ов. Пока что у нас всего один view controller(или сцена) на нашем сториборде, но скоро мы добавим еще несколько.

Это изображение иллюстрирует мини версию схемы документа, которая называется Док или Dock:

Док показывает высокоуровневые объекты сцены. Каждая сцена как минимум имеет объект View Controller, объект First Responder и Exit, но она так же может иметь и другие высокоуровневые объекты. Док очень удобен для создания подключений к аутлетам и экшенам (outlets and actions). Если вам нужно что-то подключить к view controller, то вы можете просто перетащить это что-то к иконке в Доке.

Заметка

Возможно вы и не будете особо использовать объект First Responder. Этот прокси-объект ссылается на что-либо, что имеет статус first responder в любой момент времени. В качестве примера вы можете подключить событие Touch UP Inside от кнопки к селектору cut: First Responder'а. Если в какой-то момент текстовое поле получает фокус ввода, то вы можете нажать на эту кнопку, которая теперь является first responder, для того, чтобы вырезать его текст в буфер обмена.

Запустите приложение. Оно должно выглядеть именно так, как вы его спроектировали в сториборде(но у вас оно может отличаться от нашего). Вот что получилось у нас:

Этот единственный View Controller, который вы определили как начальный View Controller. Но как наше приложение загружает его? Давайте взглянем в файл AppDelegate.swift для того, чтобы найти ответ. Вот что вы там найдете:

import UIKit
 
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
 
  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    return true
  }

Атрибут @ApplicationMain на самом верху, определяет класс AppDelegate в качестве входной точки модуля. Это требование для использования сторибордов, которые наследует делегат нашего приложения от UIResponder, который имеет свойство UIWindow. На практике все эти методы остаются пустыми. Даже application(_:didFinishLaunchingWithOptions:) просто возвращает true. Весь секрет находится в файле Info.plist. Нажмите на Info.plist (вы можете найти его в папке Supporting Files) и вы увидите вот это:

Приложения работающие со сторибордами используют UIMainStoryboardFile, который так же известен как "Main storyboard file base name", для того, чтобы определить сториборд, который должен подгружаться, когда запускается наше приложение. Когда соответствующая настройка доступна (обведенная выше), UIApplication автоматически загружает указанный файл сториборда, из этого сториборда создает экземпляр начального View Controller'а и затем установливает окно controller'а в новый объект UIWindow.

Вы так же можете видеть эту настройку в Project Settings, которая находится во вкладке General в секции Deplyment Info:

Теперь давайте создадим приложение с рейтингом, состоящее из нескольких view controller'ов.

Добавьте мне еще

Рейтинговое приложение, которое вы вот-вот создадите, будет иметь интерфейс со вкладками, состоящий из двух сцен. Со сторибордом очень просто создавать вкладки.

Давайте начнем с чистого листа. Для этого вам нужно переключиться на Main.storyboard и удалить все контролсы, с которыми вы успели поработать ранее. Так же их можно удалить все разом, если вы нажмете на схему документа, затем на пункт View Controller и нажмете клавишу Delete.

Перетащите Tab Bar Controller из библиотеки объектов на ваш холст. Может быть вам захочется увеличить до максимума ваше окно Xcode, потому что tab bar controller состоит из двух view controller'ов, которые связаны с ним самим, так что лишнее место вам не повредит. Вы можете увеличивать или уменьшать масштаб, если нажмете два раза на ваш холст. Так же вы можете менять масштаб, зажав клавишу CTRL и щелкнул левой кнопкой мышки по холсту, появится окошечко, где вы можете выбрать масштаб.

Новый Tab Bar Controller сразу поставляется нам в комплекте с двумя дополнительными view controller'ми, по одному на каждую вкладку. UITabBarController так же называется container view controller, так как он содержит второй view controller. Два остальных контейнера - это Navigation Controller и Split View Controller(вы будете использовать Navigation Controller позже).

Взаимоотношения между TabBar Controller и view controller'ми, которые он содержит, отображаются стрелочками. Встроенные взаимоотношения отображаются специальной иконкой, которая находится по середине тела стрелки:

Заметка

Если вы хотите подвинуть ваш Tab Bar Controller вместе с привязанными к нему view controller'ами, то вам нужно уменьшить масштаб, зажав клавишу ⌘ кликнуть по интересующим вас объектам, а затем просто перетащить их. (Выделенные сцены выделяются голубой каемочкой ;])

Перетащите на первый view controller лейбл (который пока что называется "item 1"), нажмите на него два раза и задайте новое имя "First Tab". Так же перетащите лейб на второй view controller ("item 2") и дайте ему имя "Second Tab". Это даст вам возможность заметить, когда меняются эти вкладки.

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

Ratings[18955:1293100] Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?

К счастью, эта проблема вполне очевидна - вы еще не установили входную точку, то есть вы не установили исходный или начальный View Controller, после того как удалили его ранее. Для того чтобы это поправить, выберите Tab Bar Controller и идите в Attribute Inspector. Поставьте галочку напротив пункта Is Initial View Controller.

На холсте стрелка указывала на удаленный View Controller, но теперь она указывает на наш Tab Bar Controller:

Это значит, что когда вы запустите приложение, то UiApplication примет Tab Bar Controller за основной экран. Запустите приложение и посмотрите сами.

Совет:

Вы можете изменить начальный view controller, просто перетащив стрелочку на другой!

Вообще у Xcode есть шаблон, который используется для создания приложений со вкладками (и называется он Tabbed Application template, что совсем не удивительно), но ведь всегда хорошо знать как все работает, так что вы можете создать свой Tab Bar Controller, когда вам это будет нужно.

Заметка:

Если вы соедините более чем пять сцен к Tab Bar Controller, то он автоматически выдаст вам "More...", когда вы запустите приложение. И это так классно!

Добавляем Table View Controller

Две сцены, которые соединены к Tab Bar Controller, являются обычными экземплярами UIViewController. Теперь вы замените первую сцену первой вкладки на UITableViewController.

Кликните на первый view controller в схеме документа (Document Outline), удалите его. Теперь из библиотеки вытащите Table View Controller на ваш холст:

Следующим шагом мы разместим Table View Controller внутри navigation controller. Выделите Table View Controller и выберите в меню Editor\Embed In\Navigation Controller. Это добавит вам еще один контроллер на ваш холст:

Конечно вы можете так же можете перетащить Navigation controller и встроить tableview , но это встраивание через команды так хорошо экономит время в распространенных случаях.

Так как Navigation Controller так же является контейнером (как и Tab Bar Controller), то у него есть своя стрелка указывающая на Table View Controller. Вы так же можете увидеть их отношения в схеме документа:

Обратите внимание, что встраивание Table View Controller'а дает ему navigation bar (видно в схеме документа). Интерфейс билдер автоматически помещает навигационную панель туда потому, что эта сцена теперь должна быть отображена внутри Navigation Controller. Это получается не настоящий объект UINavigationBar, но симулированный.

Если вы посмотрите в Attribute inspector для вашего Table View Controller, то вы увидите секцию Simulated Metrics (смоделированные показатели)

Значение "inferred" является дефолтным значением опции для сторибода, и оно означает, что панель навигации (navigation bar) будет отображаться у сцены (как бы это не звучало), если она находится внутри Navigation Controller'а. Вы можете переопределить эти настройки, если хотите, но не забывайте, что они вам нужны только для того, чтобы создать дизайн ваших экранов. Смоделированные показатели не используется в процессе исполнения, они лишь являются графическим отображением того, как будет выглядеть ваш экран в конечном счете.

Следующим шагом вам нужно подключить эти две новые сцены к вашему Tab Bar Controller. Зажмите CTRL и перетащите курсор от Tab Bar Controller к Navigation Controller. Когда вы отпустите кнопку мышки, то увидите маленькое меню. Выберите в нем опцию Relationship Segue - view controller:

Этот шаг позволит вам создать вам стрелку взаимоотношений между этими двумя сценами. Это так же является встроенным отношением, как вы видели в случае с остальными контроллерами, которые содержатся в Tab Bar Controller.

Tab Bar Controller имеет две такие связи, по одной на каждую вкладку. Сам Navigation Controller имеет встроенную связь с Table View Controller.

Когда вы создали это соединение, то новая вкладка добавилась в Tab Bar Controller, которая просто называется "Item". Для этого приложения вы, наверное, захотите, чтобы ваша новая сцена стала первой вкладкой, так что перетащите вкладки, чтобы поменять их местами:

Запустите приложение и посмотрите что у вас получилось.

Перед тем как установить какой-либо функционал нашему приложению, нам нужно немного почистить наш сториборд. Первую вкладку мы назовем "Players", вторую - "Gestures».

Как только вы соединили ваш view controller и Tab Bar Controller, вы сразу получили объект Tab Bar Item, который вы можете увидеть в схеме документа или внизу сцены. Вы используете Tab Bar Item для настройки имени вкладки и изображения, которое будет отображаться в Tab Bar Controller.

Выберите Tab Bar Item внутри Navigation Controller и в Attribute Inspector установите заголовок Players:

Переименуйте и вторую вкладку таким же образом на Gestures.

Правильно спроектированное приложение должно иметь какие-либо изображения этих вкладок. скачайте некоторые ресурсы для этого туториала. Они содержат подпапку Images. Перетащите папку в проект и выберите 'Copy if needed', нажмите Finish:

В Attribute inspector для вкладки Player выберите изображение Player.png:

Вы наверное догадались, но присвойте вкладке Gestures изображение Gestures.png.

View Controller, который встроен в Navigation Controller имеет Navigation Item, которая используется для настройки панели навигации (navigation bar). Выберите Navigation Item для Table View Controller (вы можете сделать это в схеме документа) и измените его заголовок в Attribute inspector на Players.

Заметка

Альтернативно вы можете кликнуть дважды на панель навигации для того, чтобы сменить заголовок. Обратите внимание что вам нужно кликнуть на симулированной панели навигации в Table View Controller, так как настоящая Navigation Bar находится в Navigation Controller.

Запустите приложение и восхититесь как вы создали симпатичную панель вкладок без единой строчки кода!

Прототипы ячеек

Прототипы ячеек позволяют вам легко задавать пользовательские размеры для ваших ячеек табличного окна (table view cells), напрямую из сториборда.

Table View Controller создается с пустым прототипом ячейки. Кликните на эту ячейку, чтобы выделить ее, пройдите в Attribute inspector, установите опцию Style на значение Subtitle. Это сразу поменяет внешний вид ячейки, так, что она будет включать в себя еще два лейбла.

Заметка

Иногда бывает так много всего нагромождено в сториборде, что очень сложно выделить тот элемент, который нас интересует. Что ж, есть пара вариантов как выйти из этого положения. Вы можете использовать схему документа, чтобы найти ваш объект, либо вы можете зажать CTRL + OPTION + SHIFT, это позволит вам выделить именно тот объект, который находится прямо под курсором.

Если вы использовали table views ранее и создавали ваши ячейки вручную, то вы можете узнать стиль UITableViewCellStyle.Subtitle. С прототипом ячеек вы можете либо выбрать встроенный стиль ячейки, либо создать свой собственный (что вы и так вскоре будете делать ;]).

Установите атрибут Accessory на Disclosure Indicator и Identifier на PlayerCell. Все прототипы ячеек являются объектами UITableViewCell, поэтому вы должны иметь идентификатор повторного использования.

Запустите приложение...ничего не поменялось. На самом то деле это не странно, так как вы еще не сделали источника данных для этой таблицы, чтобы она знала что отображать. Это именно то, чем мы сейчас и займемся.

Создайте новый файл проекта. Выберите шаблон Cocoa Touch Class, он находится под вкладкой iOS > Source. Назовите класс PlayerViewController и сделайте его подклассом класса UITableViewController. Так же чекбокс Also create XIB file должен быть пустым, так как вы уже имеете дизайн для вашего View Controller'а в вашем сториборде. И никаких сегодня nib! Выберите язык Swift, нажмите Next, затем Create.

Вернитесь в сториборд и выберите Table View Controller (убедитесь, что вы выбрали правильный view controller, а не один из views внутри него). Пройдите в Identity inspector, установите class на PlayerViewController. Это важный шаг для привязки сцены сториборда к вашему созданному подклассу. Не забудьте, или ваш класс не будет работать!

С этого момента при запуске вашего приложения этот table view controller из сториборда становится экземпляром класса PlayersViewController.

Наша таблица будет отображать список игроков, так что теперь вы создадите основную модель данных для вашего приложения - массив, который содержит объекты игроков. Добавьте в проект новый файл, используя шаблон Swift File, который находится в iOS > Source. Назовите его Player.

Добавьте следующий код в Player.swift:

import UIKit
 
class Player: NSObject {
  var name: String
  var game: String
  var rating: Int
 
  init(name: String, game: String, rating: Int) {
    self.name = name
    self.game = game
    self.rating = rating
    super.init()
  }
}

Ничего особенного здесь не происходит. Player является контейнером для этих трех свойств: имя игрока, игра, в которую он играет и рейтинг от 1 до 5. Затем вы создадите массив из тестовых экземпляров Player, затем мы присвоим его в PlayersViewController. Начнем с создания нового файла на основе шаблона Swift File, который назовем SampleData. Добавьте следующий код в конец SampleData.swift:

//Set up sample data
 
let playersData = [ Player(name:"Bill Evans", game:"Tic-Tac-Toe", rating: 4),
  Player(name: "Oscar Peterson", game: "Spin the Bottle", rating: 5),
  Player(name: "Dave Brubeck", game: "Texas Hold 'em Poker", rating: 2) ]

Здесь вы определили константу playersData и присвоили ей массив экземпляров класса Player.

Теперь добавьте свойство типа массива players сразу после class PlayersTableViewController в PlayersViewController.swift для хранения списка игроков:

var players: [Player] = playersData

Вы могли бы установить данные в PlayersViewController, когда определяете переменную players. Но так как данные могут быть получены позднее из файлов plist или SQL, то разумнее загружать данные за пределами view controller’а.

Теперь у вас есть массив объектов Player, вы можете продолжать подключение к источнику данных в PlayersViewController. Все еще в PlayersViewController.swift замените методы источника данных table view на следующие:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  return 1
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return players.count
}

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

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
 -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath)
    as! UITableViewCell
 
  let player = players[indexPath.row] as Player
  cell.textLabel?.text = player.name
  cell.detailTextLabel?.text = player.game
  return cell
}

Метод dequeueReusableCellWithIdentifier(_:forIndexPath:) проверяет, существуют ли ячейки, которые можно использовать еще раз. Если таких нет, то он автоматически выделит образец ячейки и вернет ее вам. Все что вам нужно - это повторный идентификатор, который вы установили в образце ячейки в сториборде, в нашем случае PlayerCell. Не забудьте установить идентификатор, или эта схема не сработает.

Запустите приложение, вы увидите, что игроки отображаются в таблице!

Написав несколько строчек кода мы можем использовать прототипы ячеек. Это же просто здорово!

Заметка:

Если вы используете только один образец ячейки, но вашей таблице нужно отображать сразу несколько, то вы можете просто добавить еще один прототип в сториборде. Или вы можете просто сделать дубликат существующей ячейки, или вы можете увеличить значение Table View Prototype Cell. Но только не забудьте дать ячейкам свой собственный идентификатор повторного использования.

Проектирование ваших собственных образцов ячеек

Использование стандартных образцов ячеек для многих приложений достаточно, но ваше приложение будет иметь еще одно изображение справой стороны ячейки, которое будет отображать рейтинг игрока (от 1 до 5 звезд). Наличие изображение стандартной ячейкой не предусмотрено, так что вам придется сделать свою.

Переключитесь на Main.storyboard, выберите образец ячейки в table view. Пройдите в Attribute inspector, выберите в поле Style атрибут Custom. Дефолтные ярлыки должны исчезнуть.

Сперва давайте сделаем наши ячейки чуть-чуть повыше. Либо просто потяните ячейки вниз за маленький белый квадратик, либо простопройдите в Size inspector и установите высоту на 55 единиц. Перетащите два Label из библиотеки в ячейку и грубо разместите их там, где были стандартные ярлыки. Поиграйте сами в Attribute inspector, чтобы у вас получились ярлыки, которые вам нравятся. Установите имя верхнего ярлыка как Name, нижнего Game.

Перетащите Image View в ячейку, разместите ее с правой стороны, рядом с ">". Задайте ее ширину равной 81 единице, высота нам не очень важна. Установите Mode на Center, таким образом ваше изображение не будет растягиваться, но будет размещаться по середине image view.

Установите ширину ярлыков равной 190 поинтам (или вручную растягивайте, или через size inspector). Но они не должны накладываться друг на друга с image view. Конечный вариант должен выглядеть вот так:

Так как у нас пользовательский дизайн ячейки, то мы можем больше не использвать свойства textLabel и detailTextLabel класса UITableViewCell. Эти свойства ссылаются на ярлыки, которых в нашей ячейке больше нет. Вместо этого мы будем использовать теги (tags) для поиска наших ярлыков.

Заметка:

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

В Attribute inspector установите значение тега (tag) для ярлыка Name равным 100, Game - 101, Image View - 102.

Затем откройте PlayersViewController.swift и добавьте в конец класса новый метод imageForRating:

func imageForRating(rating:Int) -> UIImage? {
  switch rating {
  case 1:
    return UIImage(named: "1StarSmall")
  case 2:
    return UIImage(named: "2StarsSmall")
  case 3:
    return UIImage(named: "3StarsSmall")
  case 4:
    return UIImage(named: "4StarsSmall")
  case 5:
    return UIImage(named: "5StarsSmall")
  default:
    return nil
  }
}

Все достаточно просто. Этот метод возвращает разные картинки звезд, в зависимости от рейтинга. Все еще в PlayersViewController, измените tableView(_:cellForRowAtIndexPath:) на следующий код:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) as! UITableViewCell //1
 
  let player = players[indexPath.row] as Player //2
 
  if let nameLabel = cell.viewWithTag(100) as? UILabel { //3
    nameLabel.text = player.name
  }
  if let gameLabel = cell.viewWithTag(101) as? UILabel {
    gameLabel.text = player.game
  }
  if let ratingImageView = cell.viewWithTag(102) as? UIImageView {
    ratingImageView.image = self.imageForRating(player.rating)
  }
  return cell
}

Разберем, что вы сделали:

  1. Метод dequeueReusableCellWithIdentifier высвобождает из очереди существующие ячейки с идентификатором повторного использования PlayerCell, если такие есть, но если их нет, то создает новые.
  2. Вы ищите объект Player, который вписан в соответствующую строку и присваиваете его player.
  3. Ярлыки и изображения ищутся по их тегу ячейки и заполняются данными из объекта player.

Вот и все! Теперь запустите приложение и вы увидите, что-то вроде этого:

Это не то, что мы бы хотели увидеть. Кажется, что ячейки накладываются друг на друга. Вы изменили высоту ячейки, но они не изменили своей фактической высоты. Есть два способа это поправить: вы можете изменить высоту через атрибут Row Height или реализовать метод tableView(tableView:heightForRowAtIndexPath:). В нашем случае с шаблоном все в порядке, та как мы имеем всего один тип ячеек и мы заранее знаем их высоту.

Заметка:

Вы будете использовать tableView(tableView:heightForRowAtIndexPath:), если вы не знаете высоты ячеек заранее, либо когда разные ряды (rows) таблицы имеют разную высоту.

Вернемся в Main.storyboard в Size inspector вашего table view, установите Row Hwight на 55:

Если вы запустите приложение, то теперь оно выглядит лучше!

Кстати, если вы поменяли высоту ячейки, просто перетаскивая ее, а не печатая ее значение, то ваша Row Height изменилась на значение ячейки автоматически. Так что возможно у вас нет ошибки, если вы сделали это таким образом.

Использование подкласса ячейки

Наша таблица работает неплохо, но я не большой фанат использования тегов для доступа к ярлыками и другим подвидам этого образца ячейки. Будет гораздо нагляднее, если вы соедините эти ярлыки с outlet'ами, а затем используете соответствующие свойства. Как мы это уже выяснили, вы можете.

Добавьте новый файл в проект, используя шаблон Cocoa Touch Class. Назовите его PlayerCell, сделайте его подклассом UITableViewCell. Оставьте пустым чекбокс, предлагающий создать XIB.

Добавьте свойства в класс PlayerCell, как показано ниже:

@IBOutlet weak var gameLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ratingImageView: UIImageView!

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

Вернитесь в Main.storyboard, выберите прототип PlayerCell и измените его класс на PlayerCell в Identity inspector. Теперь, каждый раз, когда вы простите источник данных вашей таблицы дать вам новую ячейку при помощи метода dequeueReusableCellWithIdentifier(_:forIndexPath:), то он вам вернет экземпляр PlayerCell, но не UITableViewCell.

Обратите внимание, что вы дали имя классу такое же как и имя идентификатора вторичного использования, то есть они оба PlayerCell, но вы можете назвать их по-разному.

Теперь соедините ваши ярлыки и image view с этими outlet'ами. Пройдите в Connections inspector в сториборде, затем выберите Player Cell или из схемы документа или с холста. Затем перетащите от outlet'а nameLabel в Connections inspector к Name объекту, либо в схеме документа, либо на холсте. Повторите то же самое для gameLabel и для ratingImageView.

Важно:

Вы должны соединить контролсы с ячейкой table view, а не с view controller! Смотрите, каждый раз, когда вы источник данных запрашивает у вашего table view новую ячейку при помощи метода tableView(tableView:heightForRowAtIndexPath:), то table view не дает вам фактической ячейки, но предоставляет копию, либо повторно выдает вам уже использованную. Это означает, что в любое время будет более чем один экземпляр класса PlayerCell. Если вы соединили ваш ярлык с outlet'ом view controller'а, то несколько копий будут пытаться использовать тот же самый outlet. Это все равно, что напрашиваться на неприятности. (С другой стороны, подключение образца ячейки к actions вашего view controller пройдет идеально. Вы бы поступили именно так, если бы у вас были пользовательские кнопки или другие UIControls в вашей ячейке.)

Альтернативой тому, чтобы использовать Connections inspector - использование CTRL-перетаскивание от PlayerCell к контролсу, где выберете из меню имя outlet’а.

Обратите внимание, что вы подключили свойства, мы можем немного упростить наш код источника данных. В PlayerViewController измените tableView(_:cellForRowAtIndexPath:) на следующее:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
 -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath)
    as! PlayerCell
 
  let player = players[indexPath.row] as Player
  cell.nameLabel.text = player.name
  cell.gameLabel.text = player.game
  cell.ratingImageView.image = imageForRating(player.rating)
  return cell
}

Вот и все. Теперь вы приводите объект полученный из dequeueReusableCellWithIdentifier в PlayerCell, а затем вы просто используете свойства, которые ведут вас к ярлыкам и изображению. Ну разве не прелесть!

Запустите ваше приложение. Оно выглядит точно так же, но на самом то деле, теперь вы используете свой подкласс ячейки!

Здесь вы можете скачать конечный вариант нашего проекта!

Что дальше?

Дальше, вы можете продолжить изучать наши туториалы по мере их появления, а также, параллельно читать перевод официальной книги по языку программирования Swift. И, для более подробного изучения языка, вы можете пройти наши курсы!

Урок подготовил: Иван Акулов

Источник урока: http://www.raywenderlich.com/81879/storyboards-tutorial-swift-part-1