Всем привет! Вы на канале школы мобильной разработки SwiftBook. Здесь мы рассказываем и показываем всё, что знаем о языке Swift и Kotlin: увлечённо, понятно и без воды.
С вами Анна Васичко и в этом и в этом видео мы поговорим про очень интересную новинку, доступную в интерфейсе на IPhone 14 Pro - Dynamic Island.
Ссылка на видео здесь
Для того чтобы использовать Dynamic Island в приложении, вам потребуется Xcode 14.1 Beta или более поздние версии.
В конечном итоге у нас получится незамысловатый проект, где по нажатию на кнопку у нас запускается обратный отсчёт времени. Переключаясь из приложения на рабочий стол, мы видим Dynamic Island, по долгому нажатию на который открывается увеличенный экран с более подробной информацией.
Dynamic Island является частью API Lives Activities, который, в свою очередь, входит в систему виджетов.
В проект на SwiftUI, который у меня уже открыт, я в первую очередь добавлю новый таргет Widget Extension с именем TimerActivityWidget.
Следующее, что мы должны сделать, это добавить в конфигурационный файл новый ключ Supports Live Activities и установить для него значение Yes.
Теперь добавим в наш проект новый Swift file, установив для него оба таргета.
В этом файле мы определим структуру, подписанную под протокол Activity Attributes.
В ней будут содержаться статичное и динамичное свойства, к которым мы будем обращаться и из виджета, и из Content View.
Для этого нам будет необходимо указать эту структуру как публичную.
Итак, файл я обозначу как LiveActivityAttributes, внутри мы должны импортировать ActivityKit.
Структура будет называться TimerAttributes, она обозначается при помощи ключевого слова public для возможности доступа их разных таргетов.
Внутри мы также создаём ещё одну структуру ContentState, которая по требованию протокола ActivityAttributes должна быть подписана под протоколы Codable и Hashable.
Внутри этой структуры мы определим динамические данные - свойство plannedDuration - временной интервал, внутри которого будет идти обратный отсчёт - с типом закрытого диапазона Date.
Также статичные данные уже в основной структуре - название действия actionName
И также при помощи typealias укажем имя TimeState для ContentState:
public struct TimerAttributes: ActivityAttributes {
public typealias TimeState = ContentState
public struct ContentState: Codable, Hashable {
var plannedDuration: ClosedRange<Date>
}
var actionName: String
}
Теперь давайте в ContentView настроим основной экран и обозначим метод по запуску таймера.
Здесь тоже не забываем импортировать ActivityKit.
И далее в body в VStack помещаем картинку в виде часов и кнопку с action и label:
VStack(spacing: 20) {
Image(systemName: "clock.fill")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.gray)
Button(action: startActivity) {
Text("Start Timer")
.foregroundColor(.red)
.font(.title)
}
}
Ниже мы объявим метод startActivity, который будем вызывать из кнопки.
Что нам потребуется в этом методе?
Я хочу указать начальное и конечное время.
Начальное будет равно Date.now
А для конечного мы извлекаем через guard опционал из Calendar.current.date с инициализатором byAdding где добавим одну минуту к начальному времени
Теперь выделим свойство под временной интервал
let startDate = Date.now
guard let endDate = Calendar.current.date(byAdding: .minute, value: 1, to: startDate) else { return }
let interval = startDate...endDate
Теперь мы создадим свойство attributes - экземпляр структуры TimerAttributes и проинициализируем свойство actionName
Далее также создадим экземпляр структуры TimeState, которая находится внутри TimerAttributes и передадим в свойство plannedDuration наш интервал
let attributes = TimerAttributes(actionName: "Cooking pasta")
let state = TimerAttributes.TimeState(plannedDuration: interval)
Чтобы собственно создать LiveActivity по нажатию на кнопку мы должны в do catch блоке запросить активность по соответствующим атрибутам и состоянию.
Прежде чем это сделать я объявлю State свойство с типом нашей активности @State private var currentActivity: Activity<TimerAttributes>?
И в do блоке буду пытаться поместить туда активность с соответствующими атрибутами и состоянием, в catch блоке выводить сообщение об ошибке:
do {
currentActivity = try Activity<TimerAttributes>.request(attributes: attributes, contentState: state)
} catch {
print(error.localizedDescription)
}
И вызовем этот метод по нажатию на кнопку
Здесь мы всё настроили, переходим в виджет
Находим структуру TimerActivityLiveActivity, где мы будем настраивать Dynamic Island
Указываем, что нам нужна конфигурация
ActivityConfiguration(for: TimerAttributes.self)
В начале здесь указывается отображение для экрана блокировки, это мы сейчас настраивать не будем, и далее собственно сам Dynamic Island и все возможные его модификации.
В начале его увеличенный вариант, который появляется при долгом тапе по нему.
По центру у меня будет текст с названием текущего действия
И текст Time Left:
VStack {
Text(context.attributes.actionName)
Text("Time Left:")
}
Далее внизу я помещу ProgressView с убывающим временем интервала:
ProgressView(timerInterval: context.state.plannedDuration)
Слева будет картинка таймера
Image(systemName: "timer")
.resizable()
.frame(width: 20, height: 20)
И справа ничего.
В компактном отображении я задам только изображение справа:
Image(systemName: "timer")
Вы можете варьировать здесь то, что будет отображаться в Dynamic Island, всё зависит от ваших задач и фантазии.
Давайте запустим проект и посмотрим, что у нас получилось
Я нажимаю на кнопку старт и перехожу на рабочий стол. Здесь зажимаю Dynamic Island и мы видим текст и ProgressView.
Вот так можно работать с Dynamic Island, это очень интересный и гибкий инструмент. На этом у меня всё, спасибо за внимание!
Подписывайся на наши соцсети: Telegram / VKontakte
Вступай в открытый чат для iOS-разработчиков: t.me/swiftbook_chat