С помощью нескольких жестов, добавленных в смесь

План А заключался в том, чтобы основать этот документ на новом перетаскивании и отпускании, выпущенном с iOS 11.4 в конце марта 2020 года. Но после быстрого составления POC стало очевидно, что новое перетаскивание не готово к использованию в прайм-тайм - на по крайней мере, так, как я хотел использовать его в этом приложении. Итак, я собираюсь использовать жесты в SwiftUI. Конечно, вам в любом случае понадобится как минимум iOS 13.

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

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

Но мне это не очень понравилось. Я хотел чего-нибудь повеселее. У меня не было планов настраивать скоринг, поэтому я решил использовать изображения. Я выбрал графику, использованную на космическом корабле Nostromo из фильма 1977 года Чужой. К сожалению, из соображений авторского права я не могу показать вам здесь свою версию. Но вот вид домино, который он будет использовать для изображений. Мы собираемся продолжить эту версию:

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

  • Мне нужно было уметь вращать домино вокруг своей оси.
  • Мне нужно было его тащить.
  • Изначально мне нужно было скрыть номинал.

Мне также нужна была большая игровая площадка, по которой я мог бы перемещаться. Достаточно потребностей - давайте сделаем еще немного кода. Я построил оболочку для вызова моего представления домино и, конечно же, назвал его Domino Wrapper.

Важное предостережение: вращение было добавлено внутри ZStack. Драг был добавлен снаружи. Если вы добавите перетаскивание + вращение к тому же контейнеру, у вас будет нежелательный побочный эффект при его повороте. Попробуйте сами.

Но я наткнулся на вторую загвоздку. Если я добавил домино на экран перед доской, мне нужно было использовать директиву zindex. Но я не смог бы использовать zindex индекс, если бы добавил домино в HStack с помощью цикла, потому что он применяется только к текущему контейнеру стека. А у меня было несколько. Итак, я добавил сначала доску, а затем домино, но затем я наткнулся на другую загвоздку: домино были добавлены в конец доски. Жизнь никогда не бывает простой, не так ли?

Я остановился на единственном оставшемся варианте: VStack. Я использовал один прямоугольник для доски и добавил домино под ним. Правление, конечно, находится в scrollView - решение, о котором я бы пожалел. Я отредактировал свои dominoWrapper и domino View, чтобы включить изображения для отображения в сделку. Вот код для основного ContentView на этом этапе:

Я также пробовал здесь еще кое-что. Я попытался использовать эффект увеличения на доске, но это не сработало, так как интерфейс затруднял использование / не отвечал. Я пробовал менять долгое нажатие на вращение. Оказалось, что это тоже перегружает интерфейс. Все это выглядело немного мрачно.

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

Я закодировал действие флиппера в методе dominoWrapper, добавив новый вид и трехмерный эффект к обоим видам домино. Я связал анимацию с жестом касания на backView (не спереди, а в виде домино). Секрет здесь был в том, как расположить взгляды.

  • Вид сзади нужно начинать с 0 градусов, так что просто смотрите лицом.
  • Вид домино нужно было начинать с 180 градусов - градуса, при котором он не будет виден нам с вами. Это немного похоже на взгляд на листок бумаги сбоку.

В процессе я обнаружил, что жесты не работают с искаженными объектами. Пища для размышлений. В любом случае…

Анимация будет запускаться жестом касания. Это увеличило бы угол обзора сзади до 180 градусов, событие, которое вызовет уменьшение угла обзора домино (спереди) до нуля. По сути, изображения поменяются местами. Эффект поворотной карты. Прошил все вместе вот таким кодом:

С кодом для метода DoDomino здесь:

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

Порадовала анимация переворачивания домино. Следующей задачей было выяснить, как смешивать и сочетать изображения домино. Ставилась задача создать пары, которые в конечном итоге совпали бы друг с другом. В моем наборе Nostromo было 35 изображений. Довольно много.

Но подождите, я пропустил шаг. Вернемся к плану. Мы строим игру в домино. Игра, которая позволит нам бросить вызов нашим друзьям в турнире.

Мне нужна была структура данных для моих домино с внешним адресом. Не куча переменных состояния с привязками. Нет, мне нужно больше. Мне нужна была структура и наблюдаемый объект, как он сейчас называется. Это должно было быть адресовано извне, потому что мне нужно было бы поговорить с другими игроками и синхронизировать наши наборы домино в игре - также как и ходы, конечно. Я приложил все усилия и создал эту надстройку с большим количеством информации, чем мне нужно… или, может быть, нет. Время покажет.

И вместе с моей структурой ObservableObject:

Примечание: willSet с последующим вызовом objectWillChange (часть структуры Combine) - это PassThroughPublisher, который вы получаете бесплатно при создании наблюдаемого объекта. Это необходимо здесь, потому что класс не будет обновлять структуру, когда она обновляется без этого вызова.

Простите, вы правы. Я еще не дал вам allocateImagesV. Вот код для этого. Это необходимо для определения того, кто с кем встречается:

Здесь есть два отдельных этапа:

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

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

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

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

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

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

Я сыграл пару игр и понял, что мне нужно больше. Когда я его закрутил, то переместился на 90 градусов. Это было хорошо, но мне нужен был способ переместить его на 180 градусов (уже показано в анимированном GIF) за один ход. Мне нужен был способ переключить концы домино одним простым действием. Я использовал другого издателя Combine, чтобы выполнить новое требование 180 градусов:

let flipDominoPublisher = PassthroughSubject<(Int,Double), Never>()

И внес эти изменения в методы DoDomino и Domino. Вот оба обновленных метода. Конечно, я сломал поворот на 90 градусов в процессе, но исправил его, изменив его на longPressGesture в методе Domino Wrapper:

Все, что мне было нужно сейчас, это перезагрузка, чтобы вы могли начать все сначала. Для этой задачи я подумал, что мне понадобится мой InsideView метод со считывателем геометрии, который я построил в предыдущем проекте Создайте настольную игру без политики. К счастью, в конце концов я этого не сделал. Нет, ответ скрывался в жесте перетаскивания. Все, что мне нужно было сделать, это перевернуть сохраненные значения, чтобы перемещать фигуры по доске. Я бы его встряхнул:

И этот код в конец Domino Wrapper метода, чтобы переместить все на свое место и обновить / перезагрузить другой набор плиток:

И этот код до конца метода DoDomino. Вам необходимо сбросить переменные состояния / привязки в их локальных средах. Подобные вещи отражают то, что я сказал о том, что упрощение делает вещи более сложными:

.onReceive(resetPublisher) { (_) in
self.flipper = 0
self.rotateAngle = 0
}

Однако была ошибка. Я заметил, что Back() не вращался, когда я повернул домино, и мне пришлось совершить кардинальный грех, превратив DominoWrapper и DoDomino в источник истины для переменной rotateAngle. Я изменил DoWrapper на привязку, но затем мне пришлось добавить ее как параметр вызова, что было неправильно. Возможно, я пропустил это, но кажется, что источник истины должен исходить от родителя к ребенку и не может быть настроен другим образом. Думаю, в этом есть смысл.

В конце концов, потребовалось кое-что исправить. Мне нужно было изменить Back() и DominoWrapper() методы, используя для этого третьего издателя. Реализованное мной решение практически идентично тому, которое я использовал для Flipper. Публиковать новый код или оставить его вам? Я пока оставлю это как вызов, но у вас уже есть 98% проекта. Это будет хорошее упражнение, чтобы исправить последнюю ошибку! Задача сделать вас лучшим программистом.

Что дальше? Мы создали игру в домино. Давай попробуем объединить эту штуку в сеть.