Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = SM6445X39C;
DEVELOPMENT_TEAM = 5FRV5G4F5S;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down Expand Up @@ -391,7 +391,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = SM6445X39C;
DEVELOPMENT_TEAM = 5FRV5G4F5S;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
Expand Down Expand Up @@ -424,7 +424,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = SM6445X39C;
DEVELOPMENT_TEAM = 5FRV5G4F5S;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand Down
14 changes: 7 additions & 7 deletions AxisContributionExample/Shared/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ struct ContentView: View {
@State private var constant: ACConstant = .init(axisMode: .horizontal, levelLabel: .number)
@State private var rowSize: CGFloat = 11
@State private var rowImageName: String = ""
@State private var dates: [Date] = []
@State private var dataSet: [Date: ACData] = [:]

var body: some View {
VStack {
Spacer()
// AxisContribution(constant: constant, source: getDates())
AxisContribution(constant: constant, source: dates) { indexSet, data in
AxisContribution(constant: constant, source: dataSet) { indexSet, data in
if rowImageName.isEmpty {
defaultBackground
}else {
Expand Down Expand Up @@ -64,13 +64,13 @@ struct ContentView: View {
}
.pickerStyle(.segmented)
Button("Refresh Dates") {
dates = getDates()
dataSet = getDates()
}
.padding()
}
.padding()
.onAppear {
dates = getDates()
dataSet = getDates()
}
}

Expand Down Expand Up @@ -103,11 +103,11 @@ struct ContentView: View {
.frame(width: rowSize, height: rowSize)
}

private func getDates() -> [Date] {
var sequenceDatas = [Date]()
private func getDates() -> [Date: ACData] {
var sequenceDatas = [Date: ACData]()
for _ in 0..<300 {
let date = Date.randomBetween(start: Date().dateHalfAyear, end: Date())
sequenceDatas.append(date)
sequenceDatas[date.startOfDay] = .init(date: date.startOfDay, count: Int.random(in: 0...10))
}
return sequenceDatas
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/AxisContribution/AxisContribution.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public struct AxisContribution<B, F>: View where B: View, F: View {
@StateObject private var store = ACDataStore()

private var constant: ACConstant = .init()
private var sourceDatas: [Date]? = nil
private var sourceDatas: [Date: ACData] = [:]
private var externalDatas: [[ACData]]? = nil

public var background: ((ACIndexSet?, ACData?) -> B)? = nil
Expand Down Expand Up @@ -190,7 +190,7 @@ public extension AxisContribution where B == EmptyView, F == EmptyView {
/// - Parameters:
/// - constant: Settings that define the contribution view.
/// - sourceDates: An array of contributed dates.
init(constant: ACConstant = .init(), source sourceDates: [Date] = []) {
init(constant: ACConstant = .init(), source sourceDates: [Date: ACData] = [:]) {
self.constant = constant
self.sourceDatas = sourceDates
}
Expand All @@ -214,7 +214,7 @@ public extension AxisContribution where B : View, F : View {
/// - background: The view that is the background of the row view.
/// - foreground: The view that is the foreground of the row view.
init(constant: ACConstant = .init(),
source sourceDates: [Date] = [],
source sourceDates: [Date: ACData] = [:],
@ViewBuilder background: @escaping (ACIndexSet?, ACData?) -> B,
@ViewBuilder foreground: @escaping (ACIndexSet?, ACData?) -> F) {
self.constant = constant
Expand All @@ -241,6 +241,6 @@ public extension AxisContribution where B : View, F : View {

struct AxisContribution_Previews: PreviewProvider {
static var previews: some View {
AxisContribution(constant: .init(), source: [])
AxisContribution(constant: .init(), source: [:])
}
}
2 changes: 1 addition & 1 deletion Sources/AxisContribution/Model/ACData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class ACData: Equatable {
/// - Parameters:
/// - date: Current date.
/// - count: The number contributed to the current date. The default value is `0`.
init(date: Date, count: Int = 0) {
public init(date: Date, count: Int = 0) {
self.date = date
self.count = count
}
Expand Down
61 changes: 31 additions & 30 deletions Sources/AxisContribution/View/ACGridStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,11 @@ import SwiftUI

/// A view that represents a grid view.
struct ACGridStack<B, F>: View where B: View, F: View {

@EnvironmentObject private var store: ACDataStore

let constant: ACConstant
var background: ((ACIndexSet?, ACData?) -> B)? = nil
var foreground: ((ACIndexSet?, ACData?) -> F)? = nil
var background: ((ACIndexSet?, ACData?) -> B)?
var foreground: ((ACIndexSet?, ACData?) -> F)?

@State private var rowSize: CGSize = .zero
@State private var titleWidth: CGFloat = .zero
Expand All @@ -43,7 +42,8 @@ struct ACGridStack<B, F>: View where B: View, F: View {
.font(store.constant.font)
}

//MARK: - Properties
// MARK: - Properties

/// Property that displays the grid view.
private var content: some View {
let spacing = store.constant.spacing
Expand All @@ -66,14 +66,14 @@ struct ACGridStack<B, F>: View where B: View, F: View {
Rectangle()
.fill(Color.clear)
.frame(width: rowSize.height, height: rowSize.height)
.overlay(getMonthTitle(column) ,alignment: .leading)
.overlay(getMonthTitle(column), alignment: .leading)
ForEach(Array(datas.enumerated()), id: \.offset) { row, data in
getRowView(column: column, row: row, data: data)
}
}
}
}
}else {
} else {
VStack(alignment: .leading, spacing: spacing) {
ZStack(alignment: .bottom) {
let size = titleWidth
Expand All @@ -100,7 +100,7 @@ struct ACGridStack<B, F>: View where B: View, F: View {
}
}

//MARK: - Methods
// MARK: - Methods

/// A method that returns a row view.
/// - Parameters:
Expand All @@ -110,15 +110,10 @@ struct ACGridStack<B, F>: View where B: View, F: View {
/// - Returns: -
private func getRowView(column: Int, row: Int, data: ACData) -> some View {
ZStack {
if data.date.startOfDay > Date().startOfDay {
background?(ACIndexSet(column: column, row: row), data)
.hidden()
}else {
background?(ACIndexSet(column: column, row: row), data)
foreground?(ACIndexSet(column: column, row: row), data)
.opacity(getOpacity(count: data.count))
.takeSize($rowSize)
}
background?(ACIndexSet(column: column, row: row), data)
foreground?(ACIndexSet(column: column, row: row), data)
.opacity(getOpacity(count: data.count))
.takeSize($rowSize)
}
}

Expand All @@ -135,15 +130,18 @@ struct ACGridStack<B, F>: View where B: View, F: View {
.fixedSize(horizontal: true, vertical: false)
.takeSize($_titleSize)
}
}else {
Text(store.datas[column][0].date.monthTitle)
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
.takeSize($_titleSize)
} else {
let date = store.datas[column][0].date
if date > constant.fromDate && date < constant.toDate {
Text(date.monthTitle)
.lineLimit(1)
.fixedSize(horizontal: true, vertical: false)
.takeSize($_titleSize)
}
}
}
}
.onChange(of: _titleSize) { newValue in
.onChange(of: _titleSize) { _ in
titleWidth = max(titleWidth, _titleSize.width)
}
}
Expand All @@ -154,29 +152,29 @@ struct ACGridStack<B, F>: View where B: View, F: View {
private func getOpacity(count: Int) -> CGFloat {
if count == 0 {
return ACLevel.zero.opacity
}else if ACLevel.first.rawValue * store.constant.levelSpacing >= count {
} else if ACLevel.first.rawValue * store.constant.levelSpacing >= count {
return ACLevel.first.opacity
}else if ACLevel.second.rawValue * store.constant.levelSpacing >= count {
} else if ACLevel.second.rawValue * store.constant.levelSpacing >= count {
return ACLevel.second.opacity
}else if ACLevel.third.rawValue * store.constant.levelSpacing >= count {
} else if ACLevel.third.rawValue * store.constant.levelSpacing >= count {
return ACLevel.third.opacity
}else if ACLevel.fourth.rawValue * store.constant.levelSpacing >= count {
} else if ACLevel.fourth.rawValue * store.constant.levelSpacing >= count {
return ACLevel.fourth.opacity
}
return 1.0
}
}

extension ACGridStack where B : View, F : View {

extension ACGridStack where B: View, F: View {
/// Initializes `ACGridStack`
/// - Parameters:
/// - constant: Settings that define the contribution view.
/// - background: The view that is the background of the row view.
/// - foreground: The view that is the foreground of the row view.
init(constant: ACConstant,
@ViewBuilder background: @escaping (ACIndexSet?, ACData?) -> B,
@ViewBuilder foreground: @escaping (ACIndexSet?, ACData?) -> F) {
@ViewBuilder foreground: @escaping (ACIndexSet?, ACData?) -> F)
{
self.constant = constant
self.background = background
self.foreground = foreground
Expand All @@ -185,6 +183,9 @@ extension ACGridStack where B : View, F : View {

struct ACGridStack_Previews: PreviewProvider {
static var previews: some View {
AxisContribution(constant: .init(), source: [])
AxisContribution(
constant: .init(),
source: [:]
)
}
}
21 changes: 6 additions & 15 deletions Sources/AxisContribution/ViewModel/ACDataProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,22 @@ public class ACDataProvider {
/// - constant: Settings that define the contribution view.
/// - sourceDates: An array of contributed dates.
/// - Returns: mapped data
public func mappedData(constant: ACConstant, source sourceDates: [Date]) -> [[ACData]] {
public func mappedData(constant: ACConstant, source sourceDates: [Date: ACData]) -> [[ACData]] {
var newDatas = [[ACData]]()
var dateWeekly = Date.datesWeekly(from: constant.fromDate, to: constant.toDate)
if constant.axisMode == .vertical {
dateWeekly = dateWeekly.reversed()
}
dateWeekly.forEach { date in
let datas = date.datesInWeek.map { date -> ACData in
let data = ACData(date: date, count: getDateCount(sourceDates: sourceDates, date: date))
return data
if let data = sourceDates[date] {
Copy link

@Maples7 Maples7 Apr 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change brings this bug #14 in. The date of sourceDates[date] doesn't necessarily need to be the start moment of the day. Therefore, sourceDates[date] is nil when toDate is Date().

Copy link

@racechao racechao Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry my bad

  1. the dictionary was introduced because I had performance concerns.
  2. you need to use Date().startOfDay as a key to archive the count for that day

I made a mistake. this pr I didn't want to introduce these changes
only

    -init(date: Date, count: Int = 0) {
    +public init(date: Date, count: Int = 0) {
        self.date = date
        self.count = count
    }

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

@Maples7 Maples7 Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@racechao That's ok, I've created a PR #13 to fix these issues.

  1. We can turn the [Date] into a [Date:ACData] internally with only 1 literation (O(n)) by doing so 3270273#diff-6eace7fd00e64a79f64bb279676278b16eab8ec0f93e83fa4d8f5a422ffd040b, I think the performance should be acceptable. The difference is whether we need to request users to make the conversion by themselves. And we'd better not to break the API policy for backward compatibility. Or, we can create a new init to support [Data:ACData] type parameter as the sourceDatas in the next new versions of this library.
  2. Yeah the date keys have been turned into startOfDay during the process mentioned in the 1st point.

FYI @jasudev for more context.

return data
} else {
return ACData(date: date, count: 0)
}
}
newDatas.append(datas)
}
return newDatas
}

/// Returns data corresponding to the date you pass in.
/// - Parameters:
/// - sourceDates: An array of contributed dates.
/// - date: The date required to return the data.
/// - Returns: -
private func getDateCount(sourceDates: [Date], date: Date) -> Int {
let dates = sourceDates.filter { d in
Calendar.current.isDate(d, inSameDayAs: date)
}
return dates.count
}
}
2 changes: 1 addition & 1 deletion Sources/AxisContribution/ViewModel/ACDataStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public class ACDataStore: ObservableObject {
/// - Parameters:
/// - constant: Settings that define the contribution view.
/// - sourceDates: An array of contributed dates.
func setup(constant: ACConstant, source sourceDates: [Date]? = nil) {
func setup(constant: ACConstant, source sourceDates: [Date: ACData]? = nil) {
self.constant = constant
if let sourceDates = sourceDates {
self.datas = ACDataProvider.shared.mappedData(constant: constant, source: sourceDates)
Expand Down