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

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

Если вы решили побольше узнать о сторибордах, то вы в правильном месте!

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

В этой части, которая является, кстати, последней, вы узнаете о сегвеях (переходах или segues), о статических ячейках таблицы (static teble view cells), а также, мы вставим один экран для добавления игроков, и экран для выбора игры!

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

Сегвеи (переходы) или Segues

Самое время добавить больше controller'ов на наш сториборд. Вы собираетесь создать экран, на котором пользователи смогут добавлять новых игроков в приложение.

Откройте Main.storyboard и перетащите из библиотеки Bar Button Item в правый слот навигационной панели сцены Players вашего table view. В Attribute inspector измените его Identifier на Add, чтобы он выглядел как стандартный символ +:

Когда пользователь нажмет эту кнопку, вы наверное захотите, чтобы приложение отобразило новое модальное окно, которое позволит внести данные нового игрока.

Перетащите новый Table View Controller на ваш холст, в право от сцены Players. Помните, что вы можете двойным щелчком увеличивать и уменьшать масштаб вашего холста! Выделите новый Table View Controller и пройдите в меню Editor > Embed in > Navigation Controller.

Есть такой трюк: выделите ваш «+», зажмите клавишу CTRL и перетащите курсор с «+», на ваш свежеиспеченный Navigation Controller. Отпустите клавишу мышки - появится маленькое меню. Выберите в нем modal.

Заметка

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

То что вы только что сделали, добавило вам новую стрелочку, которая проходит между экраном Player и Navigation Controller:

Такой тип подключения называется segue (сегвей или переход) и произносится как seg-way. Он отображает переход от одного экрана к другому. Все соединения в сториборде, с которыми мы уже познакомились, представляли собой взаимоотношения одного view controller'а, который содержит другой view controller. Переход (или сегвей) наоборот, меняет то, что уже находится на экране. Сегвеи срабатывают при нажатии на кнопки, ячейки таблицы, жесты и т.д.

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

Запустите приложение и нажмите кнопку «+». Новый table view появится у вас на экране.

Это, так называемый, "modal" сегвей (модальный переход) - новый экран полностью закрывает предыдущий. Пользователь не может взаимодействовать с предыдущим экраном, пока не закроет этот модальный экран. Чуточку позже вы увидите "push" сегвеи, которые добавляют новые экраны в навигационный стек Navigation Controller’а.

Новый экран пока что не сильно нам полезен, так как у него нет даже кнопки «закрыть», для того, чтобы вернуться на прошлый экран. Это все потому, что сегвеи работают только в одну сторону, так что наш сегвей осуществляет переход только от нашего Players к новому экрану, но не обратно - от нового к Players.

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

  1. Создайте объект, чтобы пользователь смог его выбрать.
  2. Создайте unwind метод в контроллере, в который вы хотите вернуться.
  3. Соедините метод и объект из сториборда.

Сначала давайте откроем Main.storyboard и выберем сцену нового Table View Controller(та самая, которая говорит "Root View Controller"). Измените заголовок на Add Player (даблклик на панели навигации). Затем добавьте два Bar Button Items в навигационную панель (navigation bar), установите Identifier левой кнопки на Cancel, правой кнопки на Done (так же измените ее Style с Bordered на Done).

Следующим шагом добавьте новый файл в ваш проект. Воспользуйтесь шаблоном Cocoa Touch Class и назовите его PlayerDetailsViewController, и сделайте его подклассом UITableViewController. Для того, чтобы соединить этот новый класс с вашим сторибордом, переключитесь обратно на Main.storyboard и выберите сцену Add Player. В Identity inspector установите его Class как PlayerDetailsViewController. Я все время забываю сделать этот очень важный шаг, поэтому обратите внимание, что вы тоже его не забыли сделать.

Теперь вы уже можете создать unwind сегвей. Идите в PlayersViewController.swift файл (но не в detail controller), добавьте в него unwind(далее анвинд) методы, сразу после определения класса:

@IBAction func cancelToPlayersViewController(segue:UIStoryboardSegue) {
 
}
 
@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
 
} 

Оба этих метода просто отпускают контроллер, когда их вызывают. Позже мы внесем некоторые изменения в savePlayerDetail, чтобы позволить его имени жить!

Наконец-то, переключитесь обратно на интерфейс билдер для того, чтобы привязать клавиши Done и Cancel к их методам. CTRL-ТАЩИМ от кнопок до красного значка Exit в ДОК. Выскочит маленькое меню, выберите следующее:

Обратите внимание на имя метода, который соединяем с клавишей Cancel. Когда вы создаете анвинд сегвей, то появляющийся список отображает анвинд методы всего приложения (то есть те, которые имеют вид@IBAction func methodname(segue:UIStoryboardSegue)), так что проверьте, чтобы ваши имена методов вам о чем-то, да говорили.

Запустите приложение и нажмите «+». Также протестируйте кнопки Cancel и Done. Относительно большая функциональость для такого маленького кода, не так ли?

Статические ячейки (static cells)

Когда вы покончите с этой секцией, страница «Add Player» будет выглядет вот так:

Это сгруппированный table view, но конечно вам не нужно создавать источник данных для него. Вы можете создать такой table view прямо в интерфейс билдере, и нет никакой надобности писать cellForRowAtIndexPath. А то, что позволяет нам это сделать называется static cells.

Выберите table view сцены Add Player и пройдите в Attribute inspector, чтобы поменять Content на Static Cells. Измените Style Plain на Grouped и задайте этому table view две секции.

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

Выделите верхнюю секцию Table View Section в (схеме документа). В Attributes inspector задайте имя Player Name для Header.

Перетащите новое текстовое поле (Text Field) из библиотеки в ячейку для этой секции. Растяните ее и удалите рамки, чтобы вы не видели, где она кончается, а где начинается. Установите шрифт Font на System 17.0 и уберите галочку с Adjust to Fit.

Теперь мы сделаем outlet для этогоPlayerDetailsViewController, используя Assistans Editor. Пока мы в сториборде, откройте Assistant Editor, при помощи кнопки с изображением двух скрещенных колец. Это должно автоматически открыть PlayerDetailsViewController.swift.

Выделите новое текстовое поле и перетащите его с зажатым CTRL наверх .swift файла, сразу под определением класса. Когда появится соответствующее окошечко, впишите имя nameTextField и нажмите Connect. Xcode добавит ваше свойство в определение класса и соединит его со сторибордом:

Создание outlet'ов для окон, которые находятся внутри ячеек таблицы - это именно то, что не следует делать с prototype ячейками, но со static - ОК. Всего будет только один экземпляр каждой static ячейки, так что это идеально подходит для соединения их подвидов (subviews) с outlets view controller'а. (Надеюсь вы поняли, что я имел в виду!)

Установите Style статической ячейки второй секции на Right Detail. Это предоставит вам для работы стандартную ячейку. Измените лейбл левой части на Game и в пункте Accessory поставьте Disclosure Indicator.

Точно так же как вы делали и для текстового поля «Name». Сделайте outlet справа(который отображает "Detail") и назовите его detailLabel. Лейблы (ярлыки) в ячейке таблицы так же являются обычными экземплярами UILabel. Вам может понадобится кликнуть несколько раз, прежде чем вы попадете на лейбл "Detail». После этого, вы сможете перетаскивать его вPlayerDetailsViewController.swift. Как только вы закончите у вас будет примерно такой вид:

Конечный вариант вашего дизайна будет выглядеть вот так:

Дисплеи, которые вы уже спроектировали в сториборде, имеют высоту и ширину четырех дюймового экрана iPhone5, который имеет высоту 568 поинтов. Очевидно, что ваше приложение должно работать идеально и с другими размерами дисплеев, вы можете посмотреть это в сториборде.

Откройте Assistant Editor из вашей панели инструментов и используйте в его меню опцию preview. В левой нижней части вы найдете «+», нажав на который, получите еще варианты отображения вашего приложения на выбранном дисплее. Для того, чтобы удалить не нужный вам экран, выделите его и нажмите «delete».

Для нашего приложения вам не придется делать ничего забавного. Так как мы используем table view controller'ы, то они автоматически меняются в размере, чтобы соответствовать размеру дисплея. Когда вам не нужно поддерживать поддержку дисплеев различного размера, то вы можете использовать Auto layout и Size Classes.

Запустите ваше приложение и вы увидите, что ваш экран Add Player стал пустым!

Когда вы используете статические ячейки, вашему table view controller'у не нужен источник данных. Потому что вы используете шаблон Xcode для создания класса PlayerDetailsViewController. В нем, кстати, есть кое-какой заполняющий код для источника данных, который мешает работать нашим статическим ячейкам правильно. Вот именно поэтому ваш статический контент и не отображается. Самое время пофиксить это!

Откройте PlayerDetailsViewController.swift и удалите там все, что начинается со строки:

// MARK: - Table view data source

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

Еще одна штука с этими статическими ячейками. Они работают только в UITableViewController. Даже если интерфейс билдер позволит вам добавить их в объект Table View внутри обычного UIViewController, то они не будут работать во время запуска приложения. А причина этого в том, что UITableViewController обладает дополнительной магией, которая берет на себя заботу об источнике данных для статических ячеек. И даже Xcode скажет вам о подобной ошибке: "Некорректная конфигурация: Статические Table Views могут быть использованы только, когда они встроены в экземпляры класса UITableViewController».

Прототипные ячейки, с другой стороны, работают нормально в table view, который помещен в обычный view controller. Но не работают с nib'ми. Но если вы хотите работать с прототипными ячейками или со статическими, то в любом случае вы будете использовать сториборд.

Возможен такой вариант, что вы захотите использовать внутри одного table view и прототипные, и статические ячейки, но это не так хорошо поддерживается этой SDK. Если вам действительно это нужно осуществить в вашем приложении, то для поиска ответа на этот вопрос вам лучше обратиться на форумы разработчиков Apple.

Заметка

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

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

Для того чтобы избежать этого, вам нужно, чтобы при нажатии на любую область, в пределах этого ряда (row), выходила виртуальная клавиатура. Это очень просто сделать, пройдите в PlayerDetailsViewController.swift и добавьте туда метод tableView(_:didSelectRowAtIndexPath:):

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  if indexPath.section == 0 {
    nameTextField.becomeFirstResponder()
  }
}

Заметка

При добавлении метода делегата или при переопределении метода view controller'а вы можете просто начать писать имя метода, без слова func, и увидите список всех доступных методов, которые можете использовать.

Вам так же нужно выбрать стиль Selection и изменить его с Default на None, в Attribute inspector, или ваш ряд будет выделяться, когда пользователь попадет пальцем на отступ, который есть вокруг текстового поля.

Все, мы закончили с дизайном экрана Add Player. Ну теперь давайте заставим его поработать.

Экран Add Player в работе

До этого момента мы игнорировали ряд Game и позволяли пользователям только вводить имя игрока.

Когда пользователи нажимают кнопку Cancel, то экран должен закрываться, и вся информация, введенная на предыдущем экране, должна потеряться. Ну эта часть у нас работает и без анвинд сегвея (unwind segue).

Когда пользователи нажимают Done, то должен создаваться игрок - экземпляр Player и заполнять свои свойства, а так же должен обновляться список игроков.

Метод prepareForSegue(:sender:) вызывается каждый раз, перед тем как осуществляется переход или сегвей. Вы переопределите этот метод для хранения информации, которую ввел пользователь об игроке, перед тем как закроется этот экран.

Заметка

Вы никогда не вызовете метод prepareForSegue самостоятельно, так как это сообщение от UIKit, которое позволяет вам узнать о том, что только что был осуществлен переход.

Внутри PlayerDetailsViewController.swift сначала добавьте свойство вверх класса:

var player:Player!

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

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

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: self.nameTextField.text, game: "Chess", rating: 1)
  }
}

Метод prepareForSegue(:sender:) создает новый экземпляр класса Player с дефолтными значениями игры и рейтинга. Этот метод осуществляет это только для сегвея, который имеет идентификатор SavePlayerDetail.

В Main.storyboard найдите сцену Add Player в схеме документа и выберите unwind segue, которая привязана к действию savePlayerDetail. Измените его идентификатор на "SavePlayerDetail":

Переключитесь на PlayerViewController.swift и измените метод анвинд сегвей SavePlayerDetail(segue:), чтобы он выглядел вот так:

@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  if let playerDetailsViewController = segue.sourceViewController as? PlayerDetailsViewController {
 
    //add the new player to the players array
    players.append(playerDetailsViewController.player)
 
    //update the tableView
    let indexPath = NSIndexPath(forRow: players.count-1, inSection: 0)
    tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
  }
}

Это позволяет получить ссылку на PlayerDetailsViewController через ссылку сегвея переданную в метод. Это используется для того, чтобы добавить новый экземпляр Player в массив игроков, который используется в качестве источника данных. Затем он говорит table view, что был добавлен новая строка (row)(внизу), потому что table view и ее источник данных должны быть всегда синхронизированы.

Вы можете просто использовать tableView.reloadData(), но выглядит лучше, когда вы вставляете строку с анимацией. UITableViewRowAnimation.Automatic автоматически выбирает соответствующую анимацию, зависящая от того, где вы вставляете новую строку. Очень удобно!

Попробуйте, у вас должна появиться возможность добавлять новых игроков.

Производительность

Теперь, когда у вас есть несколько контроллеров в сториборде, вас может заинтересовать вопрос производительности. Загрузить один целый сториборд за раз не так уж и сложно. Сториборд не создает экземпляры сразу всех view controller'ов, только начальный, который загружается первым. Так как исходный view controller является Tab Bar Controller, то два view controller'а, которые он содержит, так же загружаются (сцена Players из первой вкладки и вторая сцена из второй вкладки).

Другие view controller'ы не создаются до тех пор, пока вы к ним не перейдете. Когда вы закрываете view controller'ы, то они немедленно освобождаются (deallocated), таким образом, в памяти находится только используемые view controller'ы, прямо как если бы вы использовали nib файлы.

Давайте попрактикуемся. Добавьте инициализатор и деинициализатор в PlayerDetailsViewController.swift:

required init(coder aDecoder: NSCoder) {
  println("init PlayerDetailsViewController")
  super.init(coder: aDecoder)
}
 
deinit {
  println("deinit PlayerDetailsViewController")
}

Вы переопределяете init(coder:) и deinit, чтобы они отправляли вам логи в панель дебага. Теперь запустите ваше приложение и откройте страницу Add Player. Вы должны увидеть, что этот view controller не будет занимать память до тех пор, пока к нему не обратитесь.

Когда вы закрываете страничку Add Player, или нажимаете на Cancel или Done, то вы должны увидеть сообщение от метода deinit, через глобальную функцию println(). Если вы откроете снова этот же экран, то вы так же увидите сообщение от init(coder:) снова. Таким образом вы убедитесь в том, что view controller'ы загружаются только по обращению к ним.

Заметка

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

Экран выбора игры

При нажатии на строку Game на экране Add Player, должен появляться новый экран, который позволяет выбрать игру из списка. Это означает, что вы добавите новый view controller, хотя в этот раз вы просто добавите в навигацию, а не будете добавлять его модально.

Перетащите новый Table View Controller на ваш сториборд. Выберите ячейку Game на экране Add Player (только выберите ячейку целиком, а не отдельный из ее лейблов) и CTRL-тащим к новому Table View Controller'у, для создания сегвея между ними. Сделайте его Push сегвеем (под Selection Segue, но не под Accessory Action) и дайте ему идентификатор PickGame в Attribute inspector для этого сегвея.

Двойной щелчок по navigation bar и назовите этот экран Choose Game. Установите Style на Basic и дайте идентификатор reuse GameCell. Это все что вам нужно для дизайна этого экрана:

Добавьте новый swift файл в проект, используя шаблон Cocoa Touch Class и назовите его GamePickerViewController. Он будет являться подклассом UITableViewController. Вернитесь к вашей Choose Game Scene в сториборде и установите его custom class на GamePickerViewController.

Давайте предоставим некоторые данные для этого экрана, чтобы он смог их отобразить. В GamePickerViewController.swift добавьте свойство games на самый верх и переопределите функцию viewDidLoad() таким образом, чтобы начало класса выглядело вот так:

var games:[String]!
 
override func viewDidLoad() {
  super.viewDidLoad()
  games = ["Angry Birds",
           "Chess",
           "Russian Roulette",
           "Spin the Bottle",
           "Texas Hold'em Poker",
           "Tic-Tac-Toe"]
}

Вы только что добавили новый строковый массив games и заполнили его значениями в методе viewDidLoad().

Теперь, замените методы источника данных шаблона следующим кодом:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  return 1
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return games.count
}
 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as! UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
  return cell
}

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

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

Здорово, не так ли? Вам не пришлось писать и строчки кода, однако вы можете вызвать этот экран. Вы всего лишь перетащили с зажатым CTRL от статической ячейки до новой сцены и все. Единственный код, который вы написали был для заполнения контента tableView, который представляет из себя что-то более сложное, чем просто список.

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

Вверху класса GamePickerViewController, добавьте следующие свойства для хранения имени и индекса выбранной игры:

var selectedGame:String? = nil
var selectedGameIndex:Int? = nil
И замените cellForRowAtIndexPath: на:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as! UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
 
  if indexPath.row == selectedGameIndex {
    cell.accessoryType = .Checkmark
  } else {
    cell.accessoryType = .None
  }
  return cell
}

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

Теперь добавьте метод tableview(tableview:didSelectRowAtIndexPath:):

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
 
  //Other row is selected - need to deselect it
  if let index = selectedGameIndex {
    let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
    cell?.accessoryType = .None
  }
 
  selectedGameIndex = indexPath.row
  selectedGame = games[indexPath.row]
 
  //update the checkmark for the current row
  let cell = tableView.cellForRowAtIndexPath(indexPath)
  cell?.accessoryType = .Checkmark
}

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

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

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

В PlayerDetailsViewController.swift на самом верху класса добавьте свойство для хранения игры, которую вы сможете сохранить в экземпляре класса Player. Дайте дефолтное значение игры "Chess" для новых игроков.

var game:String = "Chess"

В том же файле измените viewDidLoad() так, чтобы он отображал название игры в статической ячейке таблицы:

override func viewDidLoad() {
  super.viewDidLoad()
  detailLabel.text = game
}

Добавьте метод анвинд сегвея:

@IBAction func selectedGame(segue:UIStoryboardSegue) {
  
  if let gamePickerViewController = segue.sourceViewController as? GamePickerViewController,
    
    selectedGame = gamePickerViewController.selectedGame {
      
      detailLabel.text = selectedGame
      
      game = selectedGame
      
  } 
}

Этот код исполнится, когда пользователь выберет игру из сцены Choose Game. Этот метод обновит оба ярлыка в зависимости от выбранной игры. И это так же вытеснит GamePickerViewController из стека navigation controller’а.

В Main.storyboard перетащите с зажатым CTRL от ячейки до Exit, как вы делали ранее, выберите selectedGame из появившегося списка:

Дайте анвинд сегвею идентификатор SaveSelectedGame

Запустите приложение и проверьте. Попробуйте создать нового игрока и выбрать игру.

К сожалению метод анвинд сегвея выполняется до метода tableView(_:didSelectRowAtIndexPath:), таким образом selectedGameIndex не обновляется вовремя. К счастью вы можете переопределить метод prepareForSegue(_:sender:) и завершить эту операцию до вызова анвинд сегвея.

В GamePickerViewController добавьте метод prepareForSegue(_:sender:):

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SaveSelectedGame" {
    if let cell = sender as? UITableViewCell {
      let indexPath = tableView.indexPathForCell(cell)
      selectedGameIndex = indexPath?.row
      if let index = selectedGameIndex {
        selectedGame = games[index]
      }
    }
  }
}

Так что параметр sender в prepareForSegue(_:sender:) является объектом, который инициализировал сегвей, что в нашем случае - ячейка с названием игры, на которую нажали. Так что вы можете использовать indexPath ячейки для локализации выбранной игры в массиве игры, так что после установки selectedGame доступна после анвинд сегвея.

Теперь запустите свое приложение и выберите игру и она появится в детальной информации игрока!

Затем вам нужно поменять метод prepareForSegue в PlayerDetailsViewController для того, чтобы вернуть выбранную игру, а не хардкорные шахматы. Таким образом, когда вы заканчиваете добавлять нового игрока, то его фактическая игра будет отображаться в сцене «Players».

В PlayerDetailsViewController.swift измените prepareForSegue(_:sender:) на вот это:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: nameTextField.text, game:game, rating: 1)
  }
}

Когда вы завершаете Add Player и нажимаете Done, ваш список игроков обновляется и отображает корректные данные.

Еще одна вещь. Когда вы выбираете игру, то вернитесь к экрану Add Player, а затем попытайтесь выбрать игру снова. Игра, которую вы выбрали ранее, должны иметь галочку напротив своего названия. Чтобы это сделать, вам нужно передать выбранную игру, которая хранится в PlayerDetailsViewController передать в GamePickerViewController, когда вы переходите.

Все еще в PlayerDetailsViewController.swift добавьте следующий код в конец метода prepareForSegue(_:sender:):

if segue.identifier == "PickGame" {
  if let gamePickerViewController = segue.destinationViewController as? GamePickerViewController {
    gamePickerViewController.selectedGame = game
  }
}

Обратите внимание, что у вас есть два if для проверки segue.identifier. SavePlayerDetail является анвинд сегвеем, который возвращает к списку игроков, а PickerGame переводит нас вперед на экран Game Picker. Код, который вы добавили, устанавливает selectedGame на GameViewController прямо перед тем, как загрузитсья этот view.

Откройте GamePickerViewController.swift и добавьте следующие строчки в низ метода viewDidLoad():

if let game = selectedGame {
  selectedGameIndex = find(games, game)!
}

Это берет selectedGame, который вы передали сюда из PlayerDetailsViewController и переводит это значение в индекс, который будет использоваться, для обозначения выбранной игры. find() будет искать в массиве игр ту игру, которая соответствует selectedGame и установит selectedGameIndex на индекс, на котором было найдено совпадение. Вы будете использовать этот индекс для установки галочки в соответствующей ячейке table view.

Супер! Теперь ваш экран выбора игры полностью функционирует!

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

Примите наши поздравления! Теперь вы знаете основы использования сторибордов и можете создавать приложения, которые состоят из нескольких view controller'ов!

Что дальше?

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

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

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