Гид по проекту: Apple Pie

До сих пор в этом подразделении вы многое узнали об основах Swift. Теперь пришло время применить свои знания на практике.

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

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

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

Часть первая
Создайте интерфейс

Создайте новый проект, используя шаблон приложения iOS. Назовите проект "Яблочный пирог". Эта игра предназначена для игры на iPad, а интерфейс, который вы будете создавать, не подходит для небольшого устройства iOS. Чтобы сделать приложение доступным только для iPad, выберите файл проекта в навигаторе проектов, затем в разделе Информация о развертывании снимите флажок в разделе Устройство для iPhone.  1  

Макет в раскадровке

Откройте главную раскадровку Main и используйте кнопку "Устройства", чтобы изменить устройство на iPad и изменить ориентацию на альбомный режим с помощью кнопки "Ориентация". Поскольку вы будете использовать автоматическую компоновку для создания интерфейса, он также будет работать в портретном режиме. Посмотрите еще раз на изображение готового приложения. Используя инструменты построения интерфейса, которые вы изучили до сих пор, как вы могли бы создать этот интерфейс? Вверху находится изображение, за которым следует сетка кнопок, а затем два ряда надписей, содержащих текст. Возможно, самый простой способ создать этот интерфейс - использовать вертикальный вид стека. Найдите вертикальный вид стека в библиотеке объектов и перетащите его на холст iPad.  2  

Как вы узнали из предыдущих уроков, вы можете определить положение, ширину и высоту представления стека, добавив ограничения между стеком и его супервизором. Где начинается стек и где он заканчивается? Вид изображения находится вверху, а последняя метка проходит по нижней части экрана. Сетка кнопок начинается у левого края и заканчивается у правого края. Таким образом, вид стека может быть ограничен, чтобы охватить весь экран. Выберите вид стека, затем используйте инструмент Добавить новые ограничения, чтобы добавить четыре ограничения. Установите для всех четырех полей в верхней части всплывающего окна значение 0. Загораются красные индикаторы, показывающие границы, которые ограничиваются. Когда вы закончите, нажмите "Add 4 Constraints" ("Добавить 4 ограничения").  3  

Теперь вы готовы добавить представления и элементы управления в стек. Найдите в библиотеке объектов вид изображения и перетащите его в вид стека. Поскольку это единственный элемент в стеке, он будет занимать все пространство стека. Измените "Content Mode" ("Режим содержимого") представления изображения на "Aspect Fit" ("Соответствие аспекту") в инспекторе атрибутов.  4   Это гарантирует, что ширина и высота изображения не будут искажены пропорциями вида изображения.

Добавьте изображения яблони (Tree 0.pdf...Tree 7.pdf) из папки student resources в папку Assets. После того, как они были добавлены, выберите их все и выберите "Single Scale" ("Единый масштаб") в меню "Масштабы" в инспекторе атрибутов.  5  

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

Теперь вы можете вернуться к просмотру изображения в раскадровке и обновить свойство изображения, чтобы использовать одно из трех изображений. Даже если вы обновляете изображение представления изображения в коде, это может помочь вам визуализировать свой интерфейс из раскадровки.  6  

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

Каждый из добавленных вами горизонтальных видов стека (или строк) будет одинакового размера и центрирован. Выберите недавно добавленный вид вертикального стека, затем откройте инспектор атрибутов и измените Выравнивание на Центр, а Распределение - на Равномерное заполнение. Также обеспечьте некоторый интервал между строками, установив интервал равным 5.  8  

Теперь добавьте горизонтальный вид стека в вертикальный стек.  9   Вы хотите, чтобы расстояние между столбцами было таким же, как и между строками, поэтому установите интервал равным 5. Установите как Выравнивание, так и Распределение для заполнения.  10  

Каждая строка имеет уникальное количество кнопок, первая из которых равна 10, вторая - 9, а третья - 7. Используйте библиотеку объектов, чтобы добавить кнопку в горизонтальный вид стека. Буквенные клавиши на клавиатуре обычно квадратные. Чтобы добиться такого внешнего вида, выберите кнопку и используйте кнопку Добавить новые ограничения, чтобы добавить ограничение соотношения сторон.  11   Ограничение будет добавлено, но с нежелательным соотношением.

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

Используйте инспектор атрибутов, чтобы установить шрифт кнопки на системный и установить размер шрифта равным 30, чтобы кнопку было легче читать.

Измените текст кнопки на букву Q, затем выделите и скопируйте ее в буфер обмена (Command-C). Вставьте (Command-V) новую копию кнопки в представление стека и повторяйте этот шаг до тех пор, пока представление стека не будет содержать 10 кнопок. Используйте инспектор атрибутов, чтобы обновить текст каждой кнопки до одной заглавной буквы, используя свою собственную клавиатуру в качестве ссылки.  12  

Теперь выберите горизонтальный вид стека и скопируйте его в буфер обмена. Вставьте две новые копии представления стека. Обновите каждую кнопку соответствующими буквами еще раз, используя клавиатуру в качестве ссылки — удалив все дополнительные кнопки, чтобы они соответствовали. Вы можете быстро отредактировать текст кнопки, дважды щелкнув по нему в конструкторе интерфейсов.  13  

Под сеткой кнопок во втором вертикальном стеке добавьте две метки из библиотеки объектов. Используйте инспектор атрибутов, чтобы изменить размер шрифта первой метки на 30,0, а второй - на 20,0. Все кнопки и надписи имеют так называемый внутренний размер, который определяется текстом внутри них. Автоматическая компоновка использует их собственный размер вместе с атрибутами вида стека для соответствующего размера сетки кнопок, в то время как вид изображения дерева может сжиматься и расширяться по мере необходимости.  14  

Прежде чем двигаться дальше, убедитесь, что у вас нет никаких предупреждений или ошибок в Interface Builder. Вы можете создать и запустить свое приложение, чтобы убедиться, что макет выглядит правильно на разных симуляторах iPad, или с помощью функции Просмотра как в конструкторе интерфейсов.

Создавайте выходные точки и действия

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

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

@IBOutlet var treeImageView: UIImageView!

Повторите этот процесс для двух меток. Назовите верхнюю метку correctWordLabel, а нижнюю метку scoreLabel.

@IBOutlet var correctWordLabel: UILabel!
@IBOutlet var scoreLabel: UILabel!

Это утомительно - создавать отдельную розетку для каждого UIButton. Вместо этого создайте одну розетку, в которой хранится коллекция кнопок. Начните с выбора первой кнопки с буквой Q в качестве заголовка. Управление - перетащите в определение класса, чтобы создать выход, затем отпустите кнопку мыши. В появившемся всплывающем меню измените тип подключения с розетки на Коллекцию розеток. Затем установите название коллекции розеток на letterButtons. Когда вы нажимаете кнопку Подключения, создается розетка, которая ссылается на набор кнопок.

@IBOutlet var letterButtons: [UIButton]!

Теперь нажмите и перетащите из круга рядом с коллекцией розеток на другую кнопку. Появится синий прямоугольник, указывающий на действительное соединение. Отпустите кнопку мыши, затем повторите этот шаг для каждой кнопки в сцене.  15  

Каждая кнопка также должна быть привязана к действию. Опять же, создавать отдельное действие для каждой кнопки утомительно. Вместо этого создайте одно действие, которое вызывается всеми кнопками при нажатии. Начните с выбора первой кнопки с буквой Q в качестве заголовка. Control-drag - перетащите к определению класса и отпустите кнопку мыши. В появившемся всплывающем меню измените тип подключения на Действие. Задайте для имени действия значение letterButtonPressed и измените тип аргумента на UIButton. Когда вы нажимаете кнопку Подключиться, метод создается. Всякий раз, когда нажимается кнопка с буквой, она должна быть отключена (игрок не может выбрать букву более одного раза в одном раунде).

@IBAction func letterButtonPressed(_ sender: UIButton) {
  sender.isEnabled = false
}

Зачем менять тип аргумента с Any на UIButton во всплывающем окне? Поскольку существует двадцать шесть кнопок, подключенных к одному и тому же действию, вам нужно будет использовать отправителя, чтобы определить, какая именно кнопка вызвала метод. Если sender имеет тип Any, вы не сможете получить доступ к свойству title (и это нам понадобится позже).
Управление - перетащите остальные кнопки к существующему действию. Вы будете знать, что соединение действительное, потому что при наведении курсора мыши рядом с методом появится синий прямоугольник.  16  

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

Часть вторая
Начало игры

Теперь вы готовы поработать над логикой игры Apple Pie.

Определите слова и обороты

Ваша первая задача - предоставить список слов, которые игроки должны угадать. В верхней части ViewController определите переменную с именем listOfWords. Заполните этот массив словами: названия продуктов питания, хобби, животные, предметы домашнего обихода или что-то еще. Чтобы упростить задачу, используйте только строчные буквы.

var listOfWords = [”buccaneer”, “swift”, “glorious”
“incandescent”, “bug”, “program”]

Ниже listOfWords определите константу с именем incorrectMovesAllowed, которая определяет, сколько неправильных предположений разрешено за раунд. Чем меньше число, тем труднее игроку будет выиграть. Представлено семь различных изображений яблонь, поэтому вы хотите, чтобы это значение было в диапазоне от 1 до 7.

let incorrectMovesAllowed = 7

Определите количество побед и поражений

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

var totalWins = 0
var totalLosses = 0

Начинаем Первый раунд

При запуске приложения вызывается метод viewDidLoad() ViewController. Это отличное место, чтобы начать новый раунд. Определите метод с именем newRound.

override func viewDidLoad() {
    super.viewDidLoad()
    newRound()
}
 
func newRound() {
 
}

Что значит начать новый раунд? Каждый раунд начинается с выбора нового слова и сброса количества ходов, которые игрок может сделать, на недопустимые incorrectMovesAllowed. Было бы полезно сохранить состояние игры внутри игровой структуры Game. Создайте новый файл в своем проекте, выбрав File -> New -> File (Command-N) в меню Xcode. Выберите “Swift File” в качестве шаблона, затем нажмите Далее. Назовите файл “Game”.

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

import Foundation
 
struct Game {
    var word: String
    var incorrectMovesRemaining: Int
}

Вернувшись в метод newRound(), вы можете создать новый экземпляр Game. Вы должны создать свойство, которое содержит значение текущей игры, чтобы его можно было обновлять во всем коде контроллера просмотра. Вы можете дать Game новое слово в инициализаторе, удалив первое значение из коллекции listOfWords и установив значение incorrectMovesRemaining на разрешенное вами количество ходов, сохраненное в поле incorrectMovesAllowed.

var currentGame: Game!
 
func newRound() {
  let newWord = listOfWords.removeFirst()
  currentGame = Game(word: newWord, incorrectMovesRemaining
incorrectMovesAllowed)
}

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

Теперь, когда вы начали новый раунд, вам нужно обновить интерфейс, чтобы отразить новую игру. Создайте отдельный метод с именем updateUI(), который будет обрабатывать обновления интерфейса, затем вызовите его в конце newRound. Внутри этого метода есть две части интерфейса, которые вы можете обновить с помощью кода, который вы написали до сих пор: метка оценки и представление изображения. Метка оценки использует простую интерполяцию строк для объединения totalWins и totalLosses в одну строку. Поскольку каждое из изображений дерева называется “Tree X”, где X - количество оставшихся ходов, вы можете еще раз использовать интерполяцию строк для построения имени изображения.

func newRound() {
    let newWord = listOfWords.removeFirst()
    currentGame = Game(word: newWord, incorrectMovesRemaining
    incorrectMovesAllowed)
    updateUI()
}
 
func updateUI() {
    scoreLabel.text = “Wins: \(totalWins), Losses: \
(totalLosses)”
    treeImageView.image = UIImage(named: “Tree \
    (currentGame.incorrectMovesRemaining)”)
}

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

Часть третья
Обновление Состояния игры

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

Извлечь Заголовок кнопки

В настоящее время метод letterButtonPressed(_:) отключает любую кнопку, которую игрок использовал для угадывания, но он не обновляет состояние игры. Всякий раз, когда нажимается кнопка, вы должны прочитать название кнопки и определить, есть ли эта буква в слове, которое игрок пытается угадать. Начните с чтения заголовка из свойства конфигурации кнопки. Не все кнопки имеют конфигурации или заголовки, поэтому вам нужно будет добавить восклицательные знаки, чтобы сообщить компилятору Swift, что у вашей кнопки есть конфигурация с заголовком. (Вы узнаете больше о восклицательном знаке в следующем разделе.) Вы должны использовать строчную букву, потому что гораздо проще сравнить все в нижнем регистре, а затем преобразовать ее из строки String в символ Character.

@IBAction func letterButtonPressed(_ sender: UIButton) {
    sender.isEnabled = false
    let letterString = sender.configuration!.title!
    let letter = Character(letterString.lowercased())
}

Угадай Букву

Теперь, когда у вас есть буква, которую угадал игрок, что вам с ней делать? Game управляет тем, сколько еще ходов осталось, но она не знает, какие буквы были выбраны во время раунда. Добавьте в Game коллекцию символов, которая отслеживает выбранные буквы и называется guessedLetters. Затем добавьте в Game метод, который получает Character, добавляет его в коллекцию и при необходимости обновляет incorrectMovesRemaining. (Добавив свойство guessedLetters, вам нужно будет обновить инициализацию для currentGame.)

struct Game {
    var word: String
    var incorrectMovesRemaining: Int
    var guessedLetters: [Character]
 
    mutating func playerGuessed(letter: Character) {
      guessedLetters.append(letter)
      if !word.contains(letter) {
        incorrectMovesRemaining -= 1
      }
    }
}
 
func newRound() {
    let newWord = listOfWords.removeFirst()
    currentGame = Game(word: newWord, incorrectMovesRemaining
    incorrectMovesAllowed, guessedLetters: [])
    updateUI()
}
 
@IBAction func letterButtonPressed(_ sender: UIButton) {
    sender.isEnabled = false
    let letterString = sender.title(for: .normal)!
    let letter = Character(letterString.lowercased())
    currentGame.playerGuessed(letter: letter)
    updateUI()
}

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

Часть четвертая
Создать Открытое Слово

Используя слово word и угаданные буквы guessedLetters, теперь вы можете вычислить версию слова, которая скрывает пропущенные буквы. Например, если слово для раунда - ”buccaneer“, и пользователь называл буквы ”c“, ”e“, ”b“ и ”j“, игрок должен увидеть ”b_cc__ee_" в нижней части экрана.

Для начала создайте вычисляемое свойство с именем formattedWord в определении Game. Вот один из способов вычисления formattedWord:

  • Начните с пустой строковой переменной.
  • Повторите цикл по каждому символу слова word.
  • Если символ находится в guessedLetters, преобразуйте его в строку, а затем добавьте букву в переменную.
  • В противном случае добавьте _ к переменной.

var formattedWord: String {
    var guessedWord = “”
    for letter in word {
        if guessedLetters.contains(letter) {
            guessedWord += “\(letter)”
        } else {
            guessedWord += “_”
        }
    }
    return guessedWord
}

Теперь, когда formattedWord - это свойство, которое может отображать ваш пользовательский интерфейс, попробуйте использовать его для текста currentWordLabel внутри updateUI().

func updateUI() {
    correct Word Label.text = current Game.formatted Word
    scoreLabel.text = “Wins: \(totalWins), Losses: \
    (totalLosses)”
    treeImageView.image = UIImage(named: “Tree \
    (currentGame.incorrectMovesRemaining)”)
}

Создайте и запустите свое приложение. Буквы будут добавлены в коллекцию guessedLetters по мере их выбора, а formattedWord будет пересчитываться при каждом обращении к нему в updateUI().

Вы можете заметить новую проблему: поскольку несколько подчеркиваний отображаются в интерфейсе сплошной линией, может быть трудно определить, сколько букв в слове. Решением может быть добавление пробелов во время вычисления formattedWord. Но эта проблема связана исключительно с улучшением интерфейса, а не с вмешательством в вычисляемые данные. Лучшим решением является добавление пробелов при обновлении текста correctWordLabel.

Чтобы правильно задать текст correctWordLabel, вы можете использовать Swift-метод с именем joined(separator:), который работает с массивом строк. Эта функция объединяет набор строк в одну строку, разделенную заданным значением. Вот пример:

let cast = [”Vivien”, “Marlon”, “Kim”, “Karl”]
let list = cast.joined(separator: “, “)
print(list) // “Vivien, Marlon, Kim, Karl”

Как вы можете себе представить использование метода joined(separator:) для установки correctWordLabel? Начните с преобразования массива символов в formattedWord в массив строк. Используйте цикл for для сохранения каждой из вновь созданных строк в массив [String]. Затем вы можете вызвать метод joined(separator:), чтобы объединить новую коллекцию вместе, разделенную пробелами.

func updateUI() {
    var letters = [String]()
    for letter in currentGame.formattedWord {
        letters.append(String(letter))
    }
    let wordWithSpacing = letters.joined(separator: “ “)
    correctWordLabel.text = wordWithSpacing
    scoreLabel.text = “Wins: \(totalWins), Losses: 
    \(totalLosses)”
    treeImageView.image = UIImage(named: “Tree
    \(currentGame.incorrectMovesRemaining)”)
}

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

Часть пятая
Обработать выигрыш или проигрыш

Яблочный пирог начинает казаться настоящей игрой. Раунд продвигается вместе с каждой нажатой кнопкой, но есть две очевидные проблемы. Первая заключается в том, что incorrectMovesRemaining может опуститься ниже 0, а вторая заключается в том, что игрок не может выиграть игру, даже если он правильно угадает все буквы.

Когда incorrectMovesRemaining достигает 0, totalLosses должны быть увеличены, и должен начаться новый раунд. Было бы неплохо иметь единый метод, который проверяет состояние игры, чтобы увидеть, произошел ли выигрыш или проигрыш, и если да, обновите totalWins и totalLosses. Создайте метод с именем updateGameState, который будет выполнять эту работу, и вызывайте его после каждого нажатия кнопки вместо вызова updateUI().

@IBAction func letterButtonPressed(_ sender: UIButton) {
    sender.isEnabled = false
    let letterString = sender.title(for: .normal)!
    let letter = Character(letterString.lowercased())
    currentGame.playerGuessed(letter: letter)
    updateGameState()
}
 
func updateGameState() {
 
}

Как вы определяете, выиграна ли игра, проиграна или игрок должен продолжать играть? Игра считается проигранной, если количество оставшихся неверных ходов incorrectMovesRemaining достигает 0. Когда это произойдет, увеличьте общие потери totalLosses. Вы можете определить, что игра выиграна, если игрок еще не проиграл, и если свойство word текущей игры равно formattedWord (formattedWord не будет иметь подчеркивания, если каждая буква была успешно угадана). Когда это произойдет, увеличьте общее количество выигрышей totalWins. Если игра еще не выиграна или проиграна, когда игроку должно быть разрешено продолжать угадывать, а интерфейс должен быть обновлен.

func updateGameState() {
  if currentGame.incorrectMovesRemaining == 0 {
    totalLosses += 1
  } else if currentGame.word == currentGame.formattedWord {
    totalWins += 1
  } else {
    updateUI()
  }
}

Создайте и запустите свое приложение. Правильно ли функционирует игра? Это действительно близко, но новый раунд не начинается после победы или поражения. Всякий раз, когда изменяется totalWins или totalLosses, можно начинать новый раунд, так что сейчас самое время добавить наблюдателей свойств didSet в totalWins и totalLosses.

var totalWins = 0 {
    didSet {
        newRound()
    }
}
var totalLosses = 0 {
    didSet {
        newRound()
    }
}

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

Повторно включите кнопки и исправьте сбой

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

Логика newRound должна быть немного хитрее. Если listOfWords не пуст, то вам следует выполнить ту же работу, что и ранее, но также повторно включить все кнопки. Если больше нет слов для игры, отключите все кнопки, чтобы игрок не мог продолжать играть в игру.

func newRound() {
    if !listOfWords.isEmpty {
        let newWord = listOfWords.removeFirst()
        currentGame = Game(word: newWord, 
        incorrectMovesRemaining: incorrectMovesAllowed, 
        guessedLetters: [])
        enableLetterButtons(true)
        updateUI()
    } else {
        enableLetterButtons(false)
    }
}

Метод enableLetterButtons(_:) довольно прост. Он принимает Bool в качестве аргумента и использует этот параметр для включения или отключения набора кнопок путем перебора их.

func enableLetterButtons(_ enable: Bool) {
  for button in letterButtons {
    button.isEnabled = enable
  }
}

Если вы создадите и запустите приложение, вы сможете пройти все этапы Apple Pie, пока не дойдете до конца и кнопки не будут отключены. 

Подведение итогов

Поздравляем вас с созданием вашей первой мобильной игры с помощью Swift!

Успешно завершив Apple Pie, вы продемонстрировали понимание строительных блоков языка Swift. Этот проект непростой, и вы должны гордиться тем, чего достигли. Если вам было трудно выполнить какой-либо из шагов, выделите некоторое время, чтобы восстановить Apple Pie самостоятельно, без этого руководства. Повторный просмотр укажет на области, которые вы, возможно, не поняли, и вы можете обратиться к руководству и предыдущим урокам, чтобы укрепить свои знания.

Расширить цели

Если вы хотите продолжить работу над Apple Pie, продолжайте и попробуйте добавить в игру некоторые функции. Вот несколько идей, с которыми можно поиграть. Вы можете создать большинство из этих функций, используя имеющиеся у вас знания Swift, но для некоторых из них может потребоваться использование документации Xcode. Удачи!

Испытайте себя, добавив эти функции в Apple Pie:

  • Узнайте о методе map и используйте его вместо цикла, который преобразует массив символов в массив строк в updateUI().
  • Добавьте функцию подсчета очков, которая начисляет очки за каждое правильное предположение и дополнительные очки за каждое успешное завершение слова.
  • Разрешите играть нескольким игрокам, меняя ход после каждого неверного предположения.
  • Позвольте игроку угадать полное слово с помощью клавиатуры вместо того, чтобы угадывать по одной букве за раз с помощью кнопок интерфейса.
  • Письма поддержки со специальными символами. Например, кнопка E может проверять наличие “e” и “é” в слове.
  • Раскладка клавиатуры плохо работает, когда приложение находится в режиме разделения экрана на одну треть на iPad - кнопки становятся плоскими.
  • Чтобы устранить эту проблему, используйте варианты признаков для настройки макета при компактной ширине.

 


Отрывок из книги
Develop in Swift Fundamentals
Apple Education
https://books.apple.com/ru/book/develop-in-swift-fundamentals/id1581182804

Information

Apple, the Apple logo, Apple Books, Apple TV, Apple Watch, Cocoa, Cocoa Touch, Finder, Handoff, HealthKit, iPad, iPad Pro, iPhone, iPod touch, Keynote, Mac, macOS, Numbers, Objective-C, Pages, Photo Booth, Safari, Siri, Spotlight, Swift, tvOS, watchOS, and Xcode are trademarks of Apple Inc., registered in the U.S. and other countries. App Store and iBooks Store are service marks of Apple Inc., registered in the U.S. and other countries. ​
The Bluetooth® word mark and logos are registered trademarks owned by Bluetooth SIG, Inc. and any use of such marks by Apple is under license. ​
IOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license. ​
Other product and company names mentioned herein may be trademarks of their respective companies.