Лекция 7. Shape, ViewModifier, Constants. CS193P Spring 2023.

Ниже представлен фрагмент Лекции 7 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 7 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.

Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

Что мы делаем сегодня? 

. . . . . . . . . .

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

Вы видели, как взлетела маленькая цифра +2? Это говорит о том, сколько я получил баллов.
Позвольте мне еще раз попытаться получить больше очков. Теперь хорошо, количество очков +13.

Демонстрационный пример Shape

Итак, мы собираемся создать этот маленький круглый “пирог” Pie, который ведет обратный отсчет. Мы не будем анимировать его сегодня, но мы создадим свою собственную геометрическую фигуру Shape в виде маленького “пирога”.

Для того, чтобы мы могли увидеть, что здесь происходит, я собираюсь измениться количество карт, которые у меня есть в игре, до четырех карт:

И мы положим все наши карты “лицом” вверх,  потому что мы собираемся разместить там “пирог” Pie:

Давайте начнем с нашего CardView, который вы видите здесь на экране.
Я размещу там круг Circle ( ) вместо “пирога” Pie:

И это ZStack. Они сгруппированы с помощью Group, но это все еще ZStack
И я просто помещаю Circle() позади текста Text. Мы почти закончили, правда?
Это не “пирог” Pie, а круг Circle(), и он имеет ярко-оранжевый цвет, пожалуй, слишком яркий, ведь таймер не так уж и важен. Давайте к нашему кругу Circle() добавим немного непрозрачности
.opacity ( 0.5 ):

Можно было бы снизить до .opacity ( 0.4 ):

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

Читать далее

Лекция 6. Layout, @ViewBuilder. CS193P Spring 2023.

Ниже представлен фрагмент Лекции 6 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 6 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

На сегодняшней Лекции 6, a, по сути, на всей этой неделе мы подберем несколько тем и попытаемся понять больше о том, как то, что мы делали, действительно работает.

Первая тема — это Layout, то есть расположение Views на экране. Как место на экране распределяется между всеми Views с помощью HStack, LazyVGrid, ScrollView и всех других подобных вещей? Как они решают, кому какое место достанется?
Затем у нас будет небольшая демонстрация.

Аналогичная ситуация с @ViewBuilder.
У нас есть есть такая классная штука как @ViewBuilder. Мы знаем, что это просто функция, которая возвращает some View. Но мы также знаем, что это круто и что это список Views, там есть if else, if let, switch и локальные переменные.
Так как же работает @ViewBuilder? Я не буду говорить о том, как на самом деле реализован @ViewBuilder, но я собираюсь поговорить о том, как вы его используете и как он работает.

Расположение на экране: Layout.

Как пространство на экране назначается и распределяется между всеми Views, которые там появляются? И это на самом деле это невероятно элегантная маленькая система и очень-очень простая, но в то же время с очень строгими правилами. Это делает расположение Views настолько невероятно предсказуемым, почти 100% предсказуемым.

Работа системы Layout состоит из 3-х шагов:

Первый шаг состоит, конечно, в том, что Views предоставляется некоторое пространство. Например, вашему ContentView, который находится в самом верху вашего приложения, доступен весь экран. Это отправная точка.
Но как только вы начнете создавать HStacks и VGrids, вам предлагают все меньше и меньше места по мере того, как вы спускаетесь вниз по иерархии Views.
Контейнерам Views, таким как HStack или VGrid или другим подобным им контейнерам, предлагается определенное пространство, a они в свою очередь предлагают его своим “детям”-Views с помощью определенных алгоритмов. 

Затем выполняется второй шаг, крайне важный и все дело именно в нём, он заставляет всю систему Layout действительно работать, потому что после того, как Views предложат пространство, даже если это обычный текст Text или изображение Image или что-то еще, ОНИ ВЫБИРАЮТ, какого размера они хотят быть. Единственный, кто может выбрать размер View — это само View. Невозможно принудительно указать размер View, ОНИ сами выбирают свой размер в зависимости от предложенного им пространства. Мы собираемся поговорить о том, как они это делают, но это НЕРУШИМОЕ ПРАВИЛО: Views ВЫБИРАЮТ свой собственный размер.
И именно это во многом делает систему Layout полностью предсказуемой, у вас не бывает странных случаев, когда вы действительно не понимаете, что произойдет. Никто никому ничего не навязывает.

Затем, на третьем шаге, как только Views выбрали свои размеры, они хотят вернуться в контейнер Views, который предлагал им пространство, и быть размещенными внутри него. 
Теперь, когда контейнеры Views знают все размеры, они могут размещать все вещи внутри себя, Например, HStack размещает Views, сдвигая их вправо или влево. То есть позиционирование Views полностью зависит от HStack.
Таким образом, размеры Views выбираются самими Views, а позиционирование осуществляется контейнерами Views.

Так работает система Layout. И это всегда работает таким образом, и у этого есть действительно хороший маленький протокол protocol Layout для исполнения этого «танца». Я покажу, как выглядит этот протокол, когда мы доберемся до демонстрационного примера.

Читать далее

Лекция 5. Протоколы, перечисления enum, Optional. CS193P Spring 2023.

Ниже представлен фрагмент Лекции 5 Стэнфордского курса CS193P Весна 2023 «Разработка iOS приложений с помощью SwiftUI«.
Полный русскоязычный неавторизованный конспект Лекции 5 в формате Google Doc и в виде PDF-файла, который можно скачать и использовать offline, доступны здесь.
Код находится на GitHub.

С полным перечнем Лекций и Домашних Заданий на русском языке можно познакомиться здесь.

. . . . . . . . . . . . . .

Анимация карт cards

Я сказал вам, что не собираюсь показывать вам анимацию на этой неделе. Я не собираюсь показывать вам это и на следующей неделе, даже после того, как мы потратим всю неделю на изучение анимации. Об анимации нам предстоит многое узнать. Анимация очень важна в мобильных приложениях на iPad или iPhone, и очень важно иметь хорошую анимацию.
Но сегодня
я собираюсь показать вам самую примитивную анимацию, потому что она поведет нас по пути реализации протоколов protocol, соответствия протоколам, превращения наших “не важно каких” (don’t care) Generic ТИПов в “немного важно какие” Generic ТИПы с ограничениями и все такое.

Я добавлю немного анимации к нашим картам cards.
Вот наши карты cards на UI. Вы видите, что здесь есть ScrollView, VStack — это наши карты cards, которые представляют собой сетку LazyVGrid:

Я хочу применить анимацию к моим картам cards, и собираюсь использовать значение по умолчанию .default для вида анимации. Это только один из возможных видов анимации, которую мы можем задать с помощью этого View модификатора. Мы поговорим обо всех других видах анимации позже.

Видите этот ViewModifier? Он меняет этот View, чтобы наделить его анимацией.
Здесь есть аргумент value. Какое значение мы должны ему дать? Все что угодно, любую переменную var, которую вы хотите, и анимация будет происходить только, если внутри этого View значение value изменится. Мы действительно должны указать то, что может вызвать анимацию наших карт cards, и это viewModel.cards:

По сути, когда любая из наших карт изменится, нам нужно её анимировать.
Вот почему я размещаю здесь viewModel.cards в качестве value. Итак, если наш viewModel.cards изменится, то любое из этих изменений вызовет анимацию. Например, когда я кликну на кнопке Shuffle (Перетасовать),  то произойдет анимация перетасовки карт.

Протокол Equatable

Читать далее