Переворачиваем календарь на SwiftUI

Alexey Antonov
5 min readSep 3, 2021
By Maria Yalanska from Unsplash

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

Приступим сразу к делу: открываем Xcode (если вдруг у вас его ещё нет, то срочно бегите в AppStore — он там халявный) и создаём новый проект. Выбираем iOS и тип приложения App. Продолжаем при помощи кнопки Next.

В появившемся окне вводим имя проекта и выбираем SwiftUI в пункте Interface и SwiftUI App в пункте Life Cycle. В качестве языка должен быть выбран Swift. Core Data и тесты нам не понадобятся. Ваш экран должен быть похож (кроме информации о разработчике) на скриншот ниже:

Поле Team можно не заполнять — оно предназначено для запуска приложений на реальном устройстве

На следующем экране нажимаем кнопку Create и в итоге получаем экран с новым проектом, который в общем-то уже является самостоятельным приложением. Вы можете его запустить, используя комбинацию клавиш Cmd+R, и убедиться, что приложение рабочее.

У вас откроется файл ContentView.swift, в котором уже будет написан некоторый код. Приложение Hello world, конечно, рабочее, но мы же хотим зажечь костры рябин, помните? Тогда перед строчкой struct ContentView_Previews срочно следующий код:

struct Sheet: View {  var body: some View {    HStack {      Spacer()      VStack {        Spacer()        Text("3")          .font(.largeTitle)          .foregroundColor(.red)        Text("сентября")          .font(.title)          .foregroundColor(.red)        Spacer()      }      Spacer()    }    .background(Color.white)    .cornerRadius(15)    .shadow(radius: 10)  }}

Давайте разбираться, что же тут написано. Если вы хоть раз имели дело со SwiftUI, то вы наверняка знаете, что весь интерфейс теперь представляет собой структуры (в отличие от UIKit, где используются как в известной соцсети классы). И вот мы создаём структуру календарного листа, где пишем заветную дату красным цветом (за что отвечают структуры Text и модификаторы .foregroundColor соответственно), добавляем симпатичные эффекты вроде закруглённых краёв и тени. Наш лист закончен и красив, давайте же заменим код внутри переменной body в ContentView на следующие две строчки:

Sheet()
.padding()

Запускаем!

Также можно использовать preview, чтобы не запускать симулятор

Ну что, переворачиваем?

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

@State private var isUp = true

Теперь к самому повороту. В SwiftUI есть модификатор .rotation3DEffect, как раз поворачивающий View в заданных плоскостях на заданное количество градусов. Вставьте его прямо перед строчкой .padding():

.rotation3DEffect(Angle(degrees: isUp ? 0 : 180), axis: (x: 1.0, y: 0.0, z: 0.0))

Параметры модификатора довольно логичны: угол поворота и оси поворота. И если с осью всё более или менее понятно, то про угол стоит объяснить отдельно. Конструкция isUp ? 0 : 180 называется тернарный оператор и представляет собой короткую версию оператора if:

if isUp { return 0 } else { return 180 }

Такой оператор используется очень часто, потому что разительно сокращает количество кода.

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

.onTapGesture {  withAnimation {    isUp.toggle()  }}

Чем хорош SwiftUI, так это простотой создания анимаций. Достаточно обернуть любой код в конструкцию withAnimation { }, и всё произойдёт будто по воле волшебной палочки. И вот мы запускаем, нажимаем… и видим что-то странное:

Тем временем в Австралии :)

Оно, конечно, тоже третье сентября, но какое-то неродное. Спокойно! Сейчас будем избавляться от этой австралийщины! Для этого внутри нашего Sheet мы введём переменную (над var body), которая будет сообщать состояние календаря:

let isUp: Bool

Чтобы как-то адекватно отразить наш текст (хотя он и так неотразим!), обернём оба Text в ещё один VStack, а после него вставим до боли знакомую нам конструкцию rotation3DEffect:

VStack {  Text("3")    .font(.largeTitle)    .foregroundColor(.red)  Text("сентября")    .font(.title)    .foregroundColor(.red)}.rotation3DEffect(Angle(degrees: isUp ? 0 : 180), axis: (x: 1.0, y: 0.0, z: 0.0))

Запускаем ещё раз… И всё работает, ура! Но вот возвращающийся календарь не так хорош, как перевёрнутый в нужную сторону столько раз. Давайте немного исправим этот недочёт. Для этого введём ещё одну переменную и расположим её прямо под объявлением isUp в ContentView:

@State private var sheetRotation = 0.0

Затем в rotation3DEffect меняем наш тернарный оператор на новую переменную, а в onTapGesture прибавляем 180 при каждом нажатии кнопки. Получится такой код:

.rotation3DEffect(Angle(degrees: sheetRotation),axis: (x: 1.0, y: 0.0, z: 0.0)).onTapGesture {  withAnimation {    sheetRotation += 180.0    isUp.toggle()  }}

И вот теперь всё работает так, как надо! А значит — Михаил Захарович будет доволен. Но где же обещанные костры рябин, спросите вы?

Сейчас будут! Скачиваете фото отсюда: https://unsplash.com/photos/kOgg4aUc9lc

В левой части Xcode переходите в Assets.xcassets и перетаскиваете на белый список файлов слева (где есть AccentColor и AppIcon) нашу картинку:

Не забудьте переименовать её в “ryabina”, дважды кликнув по названию.

Дальше переходим обратно в ContentView.swift и в структуре Sheet прямо над первым Text в VStack вставляем такой код:

Image("ryabina")  .resizable()  .aspectRatio(contentMode: .fit)  .padding()

В него мы вставляем картинку и умещаем её в рамки приличия, то есть в рамки нашего листочка. С небольшим отступом, конечно же.

И вот теперь мы точно всё. Существует много различных способов улучшить это простенькое приложение, так что вперёд — к изучению SwiftUI!

Всем теплоты и ласки, друзья!

А готовый проект вы можете найти на моём GitHub: https://github.com/iamalexantonov/RotatingCalendar

--

--