Перевод подготовлен в рамках курса "iOS Developer. Basic". Если вам интересно узнать о курсе больше, приходите на день открытых дверей онлайн.
![](https://habrastorage.org/getpro/habr/upload_files/678/2bf/5db/6782bf5dbbe1033043663637c13ff8fe.png)
Время от времени мне нужно визуализировать данные в виде красивых графиков. В этой статье будет показано, как рисовать графики в SwiftUI-приложении.
Создание пакета для построения графиков с нуля было невозможным из-за ограничений по времени и бюджету, поэтому мне пришлось искать уже существующие решения.
Выбор пал на SwifUI-Charts, который предлагает действительно красиво выглядящие графики и простую интеграцию.
Установка и конфигурация проекта
Сначала мы начнем с создания проекта в XCode.
![](https://habrastorage.org/getpro/habr/upload_files/ed9/415/aa2/ed9415aa2f21d9fc3e0c0dc7851363f7.png)
![](https://habrastorage.org/getpro/habr/upload_files/b16/b55/d84/b16b55d846f909ad88c92330e9dd665e.png)
Отправной точкой является стандартное SwiftUI-приложение, которое будет модифицировано для отображения графика.
![](https://habrastorage.org/getpro/habr/upload_files/f7d/ad8/2b9/f7dad82b90dc04d7a34bebeb1fc56c86.png)
На следующем этапе пакет будет добавлен, за счет открытия настройки проекта.
![](https://habrastorage.org/getpro/habr/upload_files/30d/a84/f49/30da84f49e2c4dc474a58d538ecea288.png)
Нажав на кнопку "плюс", можно добавить новый пакет. Здесь необходимо указать полный путь к репозиторию: https://github.com/spacenation/swiftui-charts.git.
![](https://habrastorage.org/getpro/habr/upload_files/bf2/c6f/46a/bf2c6f46aa329b4518d44772621ac36f.png)
На следующем экране я оставил все значения по умолчанию.
![](https://habrastorage.org/getpro/habr/upload_files/b2e/879/d9d/b2e879d9daa8e0ff401d31a1c70e9a26.png)
На последнем экране я также оставил значения по умолчанию такими, какими они были.
![](https://habrastorage.org/getpro/habr/upload_files/d44/587/e14/d44587e144824463a3b530d4e5c4289a.png)
Отображение постоянных данных
Пришло время добавить код. Для первого теста я просто взял несколько фрагментов кода из Github-Readme и добавил их в ContentView
:
import Charts
import SwiftUI
struct ContentView: View {
var body: some View {
Chart(data: [0.1, 0.3, 0.2, 0.5, 0.4, 0.9, 0.1])
.chartStyle(
LineChartStyle(.quadCurve, lineColor: .blue, lineWidth: 5)
)
}
}
При запуске этого в симуляторе будет нарисован красивый график, показывающий постоянные значения.
![](https://habrastorage.org/getpro/habr/upload_files/e2c/de9/057/e2cde9057dd28d0f5e9038905b5d6ede.png)
Это считается первым успешным тестом.
Отображение динамических данных
В той области, в которой я работаю, данные, которые мне нужно визуализировать, меняются со временем, например, в результате сенсорного ввода. Давайте расширим пример, чтобы динамически обновлять графики в соответствии с изменением данных.
В качестве "сенсора" будет использоваться класс ObservableObject
, который просто публикует случайное значение Double
каждые 500 миллисекунд.
import Foundation
class ValuePublisher: ObservableObject {
@Published var value: Double = 0.0
init() {
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
self.value = Double.random(in: 0...1.0)
}
}
}
Его нужно инстанцировать в ContentView
как переменную @State
.
@StateObject var valuePublisher = ValuePublisher()
ValuePublisher
выдает только отдельные значения, но нам необходимо, чтобы эти значения были доступны в виде списка. С этой задачей справляется простая очередь структуры данных.
struct Queue<T> {
var list = [T]()
mutating func enqueue(_ element: T) {
list.append(element)
}
mutating func dequeue() -> T? {
if !list.isEmpty {
return list.removeFirst()
} else {
return nil
}
}
func peek() -> T? {
if !list.isEmpty {
return list[0]
} else {
return nil
}
}
}
Эта очередь будет инстанцирована как переменная @State
в ContentView
@State var doubleQueue = Queue<Double>()
Основной список должен быть инициализирован при появлении представления.
.onAppear {
doubleQueue.list = [Double](repeating: 0.0, count: 50)
}
График также должен содержать информацию о списке, в котором хранятся значения.
Chart(data: doubleQueue.list)
На последнем этапе опубликованные значения ValuePublisher
должны быть добавлены в очередь, а самое старое значение из очереди должно быть удалено.
.onChange(of: valuePublisher.value) { value in
self.doubleQueue.enqueue(value)
_ = self.doubleQueue.dequeue()
}
На этом все, вот полный ContentView
, где я также немного изменил внешний вид графика.
import Charts
import SwiftUI
struct ContentView: View {
@State var doubleQueue = Queue<Double>()
@StateObject var valuePublisher = ValuePublisher()
var body: some View {
Chart(data: doubleQueue.list)
.chartStyle(
AreaChartStyle(.quadCurve, fill:
LinearGradient(gradient: .init(colors: [Color.blue.opacity(1.0), Color.blue.opacity(0.5)]), startPoint: .top, endPoint: .bottom)
)
)
.onAppear {
doubleQueue.list = [Double](repeating: 0.0, count: 50)
}
.onChange(of: valuePublisher.value) { value in
self.doubleQueue.enqueue(value)
_ = self.doubleQueue.dequeue()
}
.padding()
}
}
Вот скриншот окончательного варианта приложения
![](https://habrastorage.org/getpro/habr/upload_files/163/dd6/75a/163dd675a3f9ee4feaeeb93996be1c5c.png)
Я также загрузил видео, чтобы вы могли посмотреть, как это выглядит, когда значения обновляются динамически.
![](https://habrastorage.org/getpro/habr/upload_files/e0e/f90/ec5/e0ef90ec579cd107bc3dacb9d90b3810.png)
Видео: Графики в SwiftUI
Ресурсы