Как отобразить несколько данных за один месяц с помощью библиотеки диаграмм SwiftUI на гистограмме?

Я работаю над личным приложением и хочу обновить код. В настоящее время я использую внешнюю библиотеку кокоса под названием «Диаграммы» для создания диаграмм в своем приложении (которое находится на Swift). Тем не менее, я мог заметить, что могу реализовать встроенную библиотеку «Диаграммы» SwiftUI и реализовать ее в своем коде, но я еще не владею SwiftUI. Для этого я использую разные источники (например, это руководство на YouTube и другое руководство). Тем не менее, я хотел бы показать 3 значения в месяц, как показано на изображении под названием «01_3BarsPerMonth» (которое было создано с использованием библиотеки Cocopod «Диаграммы» в исходном проекте), но самое близкое, чего я достиг, это тот, который показан во второй ссылке, которую я вам предоставил (см. изображение «02_CurrentResult»), который в настоящее время создан с использованием собственной библиотеки «Диаграммы» SwiftUI. Я искал другие примеры, но у них есть более (и сложный) код, который на данный момент мне не нужен, и я верю, что с тем, что у меня есть на данный момент, я мог бы получить желаемые результаты.

В тестовом коде, который я сейчас использую (небольшое практическое приложение для ознакомления с библиотекой «Диаграммы» SwiftUI), это мой класс ViewController:

import SwiftUI


class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    
        setupView()
    }


// MARK: - Setup view
private func setupView() {
    view.backgroundColor = .lightGray
    
    let horizontalConstraint: CGFloat = 10
    //let controller = UIHostingController(rootView: SavingsHistory())
    let controller = UIHostingController(rootView: OrdersHistory())
    
    guard let savingsView = controller.view else { return }
    
    savingsView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(savingsView)
    
    NSLayoutConstraint.activate([
        savingsView.topAnchor.constraint(equalTo: view.topAnchor),
        savingsView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        savingsView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor, constant: horizontalConstraint),
        savingsView.trailingAnchor.constraint(equalTo: view.layoutMarginsGuide.trailingAnchor, constant: -horizontalConstraint)
    ])
}
}

И это код моей структуры:

import SwiftUI
import Charts


struct Order: Identifiable {
    var id: String = UUID().uuidString
    var amount: Int
    var day: Int
}


struct OrdersHistory: View {
    var ordersWeekOne: [Order] = [
        Order(amount: 10, day: 1),
        Order(amount: 7, day: 2),
        Order(amount: 4, day: 3),
        Order(amount: 13, day: 4),
        Order(amount: 19, day: 5),
        Order(amount: 6, day: 6),
        Order(amount: 16, day: 7)
    ]
    var ordersWeekTwo: [Order] = [
        Order(amount: 20, day: 1),
        Order(amount: 14, day: 2),
        Order(amount: 8, day: 3),
        Order(amount: 26, day: 4),
        Order(amount: 27, day: 5),
        Order(amount: 12, day: 6),
        Order(amount: 32, day: 7)
    ]
    var ordersWeekThree: [Order] = [
        Order(amount: 5, day: 1),
        Order(amount: 3, day: 2),
        Order(amount: 2, day: 3),
        Order(amount: 7, day: 4),
        Order(amount: 10, day: 5),
        Order(amount: 3, day: 6),
        Order(amount: 8, day: 7)
    ]

var body: some View {
    Chart {
        ForEach(ordersWeekOne, id: \.id) { order in
            LineMark(
                x: PlottableValue.value("Week 1", order.day),
                y: PlottableValue.value("Orders 1", order.amount),
                series: .value("Week", "One")
            )
            .foregroundStyle(Color.red)
        }
        
        ForEach(ordersWeekTwo, id: \.id) { order in
            LineMark(
                x: PlottableValue.value("Week 2", order.day),
                y: PlottableValue.value("Orders 2", order.amount)
                ,
                series: .value("Week", "Two")
            )
            .foregroundStyle(Color.green)
        }
        
        ForEach(ordersWeekThree, id: \.id) { order in
            LineMark(
                x: PlottableValue.value("Week 3", order.day),
                y: PlottableValue.value("Orders 3", order.amount)
                ,
                series: .value("Week", "Three")
            )
            .foregroundStyle(Color.black)
        }
    }
}
}

Я пытался изменить приведенный выше пример, но всегда получаю сообщение об ошибке, например «[OrdersHistory] должно соответствовать Identifying» или что-то в этом роде, когда я пытаюсь создать объект типа [[OrdersHistory]].

Что я могу изменить, чтобы получить результат, как на первом изображении (например, 3 значения для января).

Я ценю ваш отзыв. С уважением.

Это очень сбивает с толку. Ваш исходный код создает гистограмму, а код SwiftUI создает линейную диаграмму? Для линейного графика результат мне кажется правильным. Вам нужен линейный график или гистограмма?

Sweeper 13.04.2024 02:48

Привет, уборщик. Спасибо за вопрос. Используя библиотеку диаграмм SwiftUI, я хочу создать гистограмму, похожую на изображение «Облученность на плоской поверхности», которым я поделился в своем первом вопросе. Итак, в моем практическом коде у меня есть 3 массива с данными (ordersWeekOne,ordersWeekTwo иorderWeekThree), и я хотел бы создать диаграмму, используя эти данные, и гистограмма должна выглядеть как упомянутое ранее изображение. Если я изменю линейную диаграмму на гистограмму, я получу сложную гистограмму, а это не то, что мне нужно. Надеюсь, я прояснил ваше замешательство. С уважением.

J_A_F_Casillas 14.04.2024 16:47

Я понимаю. Кажется, вам просто нужно передать .unstacked параметру stacking:BarMark.init.

Sweeper 14.04.2024 16:55

Привет, уборщик. Я уже пробовал, но он показывает сложенную гистограмму, где красная полоса (соответствующая массиву «ordersWeekOne») «исчезает». Еще раз спасибо за ваш ответ.

J_A_F_Casillas 14.04.2024 20:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
199
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы начал с рефакторинга вашего типа Order, включив в него номер недели.

struct Order: Identifiable {
    var id: String = UUID().uuidString
    var week: Int
    var amount: Int
    var day: Int
}

Тогда вы можете использовать один массив в качестве источника данных.

var orders: [Order] = [
    Order(week: 1, amount: 10, day: 1),
    Order(week: 1, amount: 7, day: 2),
    ...
    Order(week: 3, amount: 3, day: 6),
    Order(week: 3, amount: 8, day: 7)
]

Тогда нам нужно использовать строки для наших меток/групп вместо Int.

extension Order {
    var weekLabel: String {
        "\(week)"
    }

    var dayLabel: String {
        Calendar.current.weekdaySymbols[day-1]
    }
}

И теперь мы можем использовать эти метки и массив для диаграммы.

Chart {
    ForEach(orders) { order in
        BarMark(
            x: .value("Day", order.dayLabel),
            y: .value("Amount", order.amount)
        )
        .foregroundStyle(by: .value("Week", order.weekLabel))
        .position(by: .value("Week", order.weekLabel))
    }

}

Если вы хотите работать с тремя разными массивами, вы можете объединить их в массив массивов.

let ordersWeek: [[Order]] = [
    [
        Order(amount: 10, day: 1),
        Order(amount: 7, day: 2),
        //...
    ], [
        Order(amount: 20, day: 1),
        Order(amount: 14, day: 2),
        //...
    ], [
        Order(amount: 5, day: 1),
        Order(amount: 3, day: 2),
        //...
    ]
]

А затем используйте укладку, как предложено @Sweeper.

Chart {
    ForEach(ordersWeek.indices, id: \.self) { index in
        ForEach(ordersWeek[index]) { order in
            BarMark(
                x: .value("Day", order.dayLabel),
                y: .value("Amount", order.amount),
                stacking: MarkStackingMethod.standard)
            .foregroundStyle(by: .value("Week", "\(index + 1)"))
            .position(by: .value("Week", "\(index)"))
        }
    }
}

Альтернативный способ создания массива массивов из исходных свойств массива — использовать вычисляемое свойство:

var ordersWeek: [[Order]] {
    [ordersWeekOne, ordersWeekTwo, ordersWeekThree]
}

Привет Йоаким. Спасибо за ваш возможный ответ.

J_A_F_Casillas 14.04.2024 17:09

Это действительно один из способов получить желаемый результат. Однако я также хотел бы знать, как это сделать, используя три массива, которые я изначально опубликовал в вопросе. Я знаю, что мне нужно будет использовать вложенный ForEach (как в более сложных примерах, которые я видел и упоминал в исходном вопросе ранее). Знаете, чтобы получить «больше оружия», используя общие знания от тех, кто знает больше, как в вашем случае. Я более опытен в использовании UIKit, SwiftUI не моя опора, но сейчас я его изучаю. Еще раз спасибо за любую возможную помощь

J_A_F_Casillas 14.04.2024 17:16

@J_A_F_Casillas Ответ дополнен альтернативным решением

Joakim Danielson 15.04.2024 15:15

Спасибо @Joakim и Sweepers за сотрудничество. Я внес некоторые небольшие изменения (например, сделал «day: String» вместо «day: Int» в структуре «struct Order: Identifying»), но с помощью кода, который вы любезно предоставили, я смог добиться того, чего хотел. Большое спасибо :)

J_A_F_Casillas 16.04.2024 10:51

И последний вопрос: как я могу изменить/настроить цвет диаграмм? Я попробовал использовать .chartForegroundStyleScale([ "Week 1": .green, "Week 2": .red, "Week 3": .blue ]) после строки Chart {, но это не работает. Еще раз спасибо.

J_A_F_Casillas 16.04.2024 11:00

Изучите модификатор ChartForegroundStyleScale для Chart.

Joakim Danielson 16.04.2024 11:37

@J_A_F_Casillas Не забудьте принять ответ, поскольку он помог вам достичь того, чего вы хотели.

Marcy 16.04.2024 17:25

Другие вопросы по теме