top view will shrink and fade into another view
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
1C683FCB2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
|
1C683FCB2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
|
||||||
1C683FCC2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
|
1C683FCC2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; };
|
||||||
1C6B377A2799B78A001EF820 /* BGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B37792799B78A001EF820 /* BGView.swift */; };
|
1C6B377A2799B78A001EF820 /* BGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C6B37792799B78A001EF820 /* BGView.swift */; };
|
||||||
|
1C7190A427A483D300388EDC /* SmallHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C7190A327A483D300388EDC /* SmallHeaderView.swift */; };
|
||||||
1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C744F2B278CE15600953A57 /* AppDelegate.swift */; };
|
1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C744F2B278CE15600953A57 /* AppDelegate.swift */; };
|
||||||
1C747CC9279F06EB00762CBD /* CloudKitSyncMonitor in Frameworks */ = {isa = PBXBuildFile; productRef = 1C747CC8279F06EB00762CBD /* CloudKitSyncMonitor */; };
|
1C747CC9279F06EB00762CBD /* CloudKitSyncMonitor in Frameworks */ = {isa = PBXBuildFile; productRef = 1C747CC8279F06EB00762CBD /* CloudKitSyncMonitor */; };
|
||||||
1CA037702799FFA600D26164 /* ContentModeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */; };
|
1CA037702799FFA600D26164 /* ContentModeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */; };
|
||||||
@@ -128,6 +129,7 @@
|
|||||||
1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsStore.swift; sourceTree = "<group>"; };
|
1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsStore.swift; sourceTree = "<group>"; };
|
||||||
1C683FC92792281400745862 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
1C683FC92792281400745862 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
||||||
1C6B37792799B78A001EF820 /* BGView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGView.swift; sourceTree = "<group>"; };
|
1C6B37792799B78A001EF820 /* BGView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGView.swift; sourceTree = "<group>"; };
|
||||||
|
1C7190A327A483D300388EDC /* SmallHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallHeaderView.swift; sourceTree = "<group>"; };
|
||||||
1C744F2B278CE15600953A57 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
1C744F2B278CE15600953A57 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentModeViewModel.swift; sourceTree = "<group>"; };
|
1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentModeViewModel.swift; sourceTree = "<group>"; };
|
||||||
1CA03772279A293D00D26164 /* OnboardingTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTime.swift; sourceTree = "<group>"; };
|
1CA03772279A293D00D26164 /* OnboardingTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTime.swift; sourceTree = "<group>"; };
|
||||||
@@ -336,6 +338,7 @@
|
|||||||
1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */,
|
1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */,
|
||||||
1CD90B32278C7E38001C4FEA /* SettingsView.swift */,
|
1CD90B32278C7E38001C4FEA /* SettingsView.swift */,
|
||||||
1C6B37792799B78A001EF820 /* BGView.swift */,
|
1C6B37792799B78A001EF820 /* BGView.swift */,
|
||||||
|
1C7190A327A483D300388EDC /* SmallHeaderView.swift */,
|
||||||
);
|
);
|
||||||
path = views;
|
path = views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -593,6 +596,7 @@
|
|||||||
1CD90B3D278C7E38001C4FEA /* ContentView.swift in Sources */,
|
1CD90B3D278C7E38001C4FEA /* ContentView.swift in Sources */,
|
||||||
1CD90B3F278C7E38001C4FEA /* HeaderStatsView.swift in Sources */,
|
1CD90B3F278C7E38001C4FEA /* HeaderStatsView.swift in Sources */,
|
||||||
1CD90B3B278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */,
|
1CD90B3B278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */,
|
||||||
|
1C7190A427A483D300388EDC /* SmallHeaderView.swift in Sources */,
|
||||||
1CC469AC27907D48003E0C6E /* DayChartView.swift in Sources */,
|
1CC469AC27907D48003E0C6E /* DayChartView.swift in Sources */,
|
||||||
1CD90B37278C7E38001C4FEA /* SettingsView.swift in Sources */,
|
1CD90B37278C7E38001C4FEA /* SettingsView.swift in Sources */,
|
||||||
1C26190327960CE500FDC148 /* ChartDataBuildable.swift in Sources */,
|
1C26190327960CE500FDC148 /* ChartDataBuildable.swift in Sources */,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ struct FeelsApp: App {
|
|||||||
}.onChange(of: scenePhase) { phase in
|
}.onChange(of: scenePhase) { phase in
|
||||||
if phase == .background {
|
if phase == .background {
|
||||||
BGTask.scheduleBackgroundProcessing()
|
BGTask.scheduleBackgroundProcessing()
|
||||||
print("background")
|
|
||||||
WidgetCenter.shared.reloadAllTimelines()
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ struct AddMoodHeaderView: View {
|
|||||||
}, label: {
|
}, label: {
|
||||||
mood.icon
|
mood.icon
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 50, height: 50, alignment: .center)
|
.frame(width: CGFloat(50), height: CGFloat(50), alignment: .center)
|
||||||
.foregroundColor(mood.color)
|
.foregroundColor(mood.color)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -42,13 +42,12 @@ struct AddMoodHeaderView: View {
|
|||||||
}.frame(minWidth: 0, maxWidth: .infinity)
|
}.frame(minWidth: 0, maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding([.leading, .trailing, .bottom])
|
|
||||||
}
|
}
|
||||||
|
.padding([.leading, .trailing, .bottom])
|
||||||
}
|
}
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
|
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
|
||||||
.frame(minHeight: 85, maxHeight: 140)
|
.frame(minHeight: 88, maxHeight: 150)
|
||||||
.frame(minWidth: 0, maxWidth: .infinity)
|
.frame(minWidth: 0, maxWidth: .infinity)
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getTitle() -> String {
|
private func getTitle() -> String {
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ struct ContentView: View {
|
|||||||
@State private var showTodayInput = true
|
@State private var showTodayInput = true
|
||||||
@State private var selectedEntry: MoodEntry?
|
@State private var selectedEntry: MoodEntry?
|
||||||
@State private var showUpdateEntryAlert = false
|
@State private var showUpdateEntryAlert = false
|
||||||
|
@State private var headerHeight: CGFloat = 150
|
||||||
|
|
||||||
|
let minHeaderHeight = 88.0
|
||||||
|
let maxHeaderHeight = 150.0
|
||||||
|
@State private var headerOpacity: Double = 1.0
|
||||||
|
|
||||||
@ObservedObject var viewModel = ContentModeViewModel()
|
@ObservedObject var viewModel = ContentModeViewModel()
|
||||||
|
|
||||||
@@ -106,50 +111,86 @@ struct ContentView: View {
|
|||||||
return formatter.string(from: NSNumber(integerLiteral: day)) ?? ""
|
return formatter.string(from: NSNumber(integerLiteral: day)) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
private var listView: some View {
|
func calcuateViewAlpha() {
|
||||||
VStack {
|
let perc = (((Double(headerHeight) - minHeaderHeight) * 100) / (maxHeaderHeight - minHeaderHeight)) / 100
|
||||||
List {
|
headerOpacity = perc
|
||||||
// for reach year
|
}
|
||||||
ForEach(viewModel.grouped.sorted(by: {
|
|
||||||
$0.key > $1.key
|
|
||||||
}), id: \.key) { year, months in
|
|
||||||
|
|
||||||
// for reach month
|
func calculateHeight(minHeight: CGFloat, maxHeight: CGFloat, yOffset: CGFloat) {
|
||||||
ForEach(months.sorted(by: {
|
calcuateViewAlpha()
|
||||||
|
|
||||||
|
let newValue = maxHeight + yOffset
|
||||||
|
// If scrolling up, yOffset will be a negative number
|
||||||
|
if newValue < minHeight {
|
||||||
|
// SCROLLING UP
|
||||||
|
// Never go smaller than our minimum height
|
||||||
|
headerHeight = minHeight
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if newValue > maxHeight {
|
||||||
|
// SCROLLING UP
|
||||||
|
// Never go smaller than our minimum height
|
||||||
|
headerHeight = maxHeight
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCROLLING DOWN
|
||||||
|
headerHeight = newValue
|
||||||
|
}
|
||||||
|
|
||||||
|
private var listView: some View {
|
||||||
|
ScrollView {
|
||||||
|
ZStack {
|
||||||
|
Color(.systemBackground).edgesIgnoringSafeArea(.bottom)
|
||||||
|
.cornerRadius(10)
|
||||||
|
|
||||||
|
LazyVStack {
|
||||||
|
// for reach year
|
||||||
|
ForEach(viewModel.grouped.sorted(by: {
|
||||||
$0.key > $1.key
|
$0.key > $1.key
|
||||||
}), id: \.key) { month, entries in
|
}), id: \.key) { year, months in
|
||||||
Section(header:
|
|
||||||
HStack{
|
// for reach month
|
||||||
Text(monthName(fromMonthInt: month))
|
ForEach(months.sorted(by: {
|
||||||
.font(.title2)
|
$0.key > $1.key
|
||||||
.foregroundColor(Color(UIColor.label))
|
}), id: \.key) { month, entries in
|
||||||
Text(String(year))
|
Section(header:
|
||||||
.font(.title2)
|
HStack{
|
||||||
.foregroundColor(Color(UIColor.label))
|
Text(monthName(fromMonthInt: month))
|
||||||
}) {
|
.font(.title2)
|
||||||
// for reach all entries
|
.foregroundColor(Color(UIColor.label))
|
||||||
ForEach(entries.sorted(by: {
|
Text(String(year))
|
||||||
return $0.forDate! > $1.forDate!
|
.font(.title2)
|
||||||
}), id: \.self) { entry in
|
.foregroundColor(Color(UIColor.label))
|
||||||
entryListView(entry: entry)
|
}) {
|
||||||
.onTapGesture(perform: {
|
// for reach all entries
|
||||||
selectedEntry = entry
|
ForEach(entries.sorted(by: {
|
||||||
showUpdateEntryAlert = true
|
return $0.forDate! > $1.forDate!
|
||||||
})
|
}), id: \.self) { entry in
|
||||||
}.onDelete(perform: { offsets in
|
entryListView(entry: entry)
|
||||||
withAnimation {
|
.onTapGesture(perform: {
|
||||||
viewModel.delete(offsets: offsets, inMonth: month, inYear: year)
|
selectedEntry = entry
|
||||||
}
|
showUpdateEntryAlert = true
|
||||||
})
|
})
|
||||||
|
}.onDelete(perform: { offsets in
|
||||||
|
withAnimation {
|
||||||
|
viewModel.delete(offsets: offsets, inMonth: month, inYear: year)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GeometryReader { proxy in
|
||||||
|
let offset = proxy.frame(in: .named("scroll")).minY
|
||||||
|
Color.clear.preference(key: ViewOffsetKey.self, value: offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.background(Color.clear.ignoresSafeArea())
|
}
|
||||||
.onAppear {
|
.coordinateSpace(name: "scroll")
|
||||||
// Set the default to clear
|
.onPreferenceChange(ViewOffsetKey.self) { value in
|
||||||
UITableView.appearance().backgroundColor = .clear
|
calculateHeight(minHeight: 88, maxHeight: 180, yOffset: value)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,12 +232,13 @@ struct ContentView: View {
|
|||||||
viewModel.add(mood: mood, forDate: date)
|
viewModel.add(mood: mood, forDate: date)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.frame(height: 180)
|
.frame(height: headerHeight)
|
||||||
.frame(minWidth: 0, maxWidth: .infinity)
|
.frame(minWidth: 0, maxWidth: .infinity)
|
||||||
} else {
|
} else {
|
||||||
HeaderStatsView(fakeData: false, backDays: 30)
|
HeaderStatsView(fakeData: false, backDays: 30)
|
||||||
.frame(height: 180)
|
.frame(height: headerHeight)
|
||||||
// should match backDays above
|
|
||||||
|
// should match backDays above
|
||||||
Text(String(format: String(localized: "content_view_header_title"), 30))
|
Text(String(format: String(localized: "content_view_header_title"), 30))
|
||||||
.font(.body)
|
.font(.body)
|
||||||
.foregroundColor(Color(UIColor.systemGray))
|
.foregroundColor(Color(UIColor.systemGray))
|
||||||
@@ -233,26 +275,50 @@ struct ContentView: View {
|
|||||||
|
|
||||||
private var mainView: some View {
|
private var mainView: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
|
|
||||||
BGView().equatable()
|
BGView().equatable()
|
||||||
|
|
||||||
VStack{
|
VStack{
|
||||||
settingsButtonView
|
settingsButtonView
|
||||||
.padding(.top, 50)
|
.padding(.top, 50)
|
||||||
|
|
||||||
if viewModel.hasNoData {
|
if viewModel.hasNoData {
|
||||||
Spacer()
|
Spacer()
|
||||||
emptyView
|
emptyView
|
||||||
Spacer()
|
Spacer()
|
||||||
} else {
|
} else {
|
||||||
headerView
|
ZStack {
|
||||||
listView
|
VStack {
|
||||||
|
headerView
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.opacity(headerOpacity)
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
SmallRollUpHeaderView(fakeData: false, backDays: 30)
|
||||||
|
.padding([.leading, .trailing])
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.opacity(1 - headerOpacity)
|
||||||
|
|
||||||
|
listView
|
||||||
|
.padding([.leading, .trailing])
|
||||||
|
.padding(.top, headerHeight+10)
|
||||||
|
.padding(.bottom, 60)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ViewOffsetKey: PreferenceKey {
|
||||||
|
typealias Value = CGFloat
|
||||||
|
static var defaultValue = CGFloat.zero
|
||||||
|
static func reduce(value: inout Value, nextValue: () -> Value) {
|
||||||
|
value += nextValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let itemFormatter: DateFormatter = {
|
private let itemFormatter: DateFormatter = {
|
||||||
let formatter = DateFormatter()
|
let formatter = DateFormatter()
|
||||||
formatter.dateStyle = .short
|
formatter.dateStyle = .short
|
||||||
|
|||||||
@@ -206,7 +206,6 @@ struct SettingsView: View {
|
|||||||
Toggle(String(localized: "settings_use_cloudkit_title"),
|
Toggle(String(localized: "settings_use_cloudkit_title"),
|
||||||
isOn: $useCloudKit)
|
isOn: $useCloudKit)
|
||||||
.onChange(of: useCloudKit) { value in
|
.onChange(of: useCloudKit) { value in
|
||||||
print(value)
|
|
||||||
PersistenceController.shared.switchContainer()
|
PersistenceController.shared.switchContainer()
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
|
|||||||
65
Shared/views/SmallHeaderView.swift
Normal file
65
Shared/views/SmallHeaderView.swift
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// SmallHeaderView.swift
|
||||||
|
// Feels (iOS)
|
||||||
|
//
|
||||||
|
// Created by Trey Tartt on 1/28/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SmallRollUpHeaderView: View {
|
||||||
|
var entries = [(Mood, Int)]()
|
||||||
|
let backDays: Int
|
||||||
|
|
||||||
|
init(fakeData: Bool, backDays: Int) {
|
||||||
|
self.backDays = backDays
|
||||||
|
var moodEntries: [MoodEntry]?
|
||||||
|
|
||||||
|
if fakeData {
|
||||||
|
moodEntries = PersistenceController.shared.randomEntries(count: 10)
|
||||||
|
} else {
|
||||||
|
var daysAgo = Calendar.current.date(byAdding: .day, value: -backDays, to: Date())!
|
||||||
|
daysAgo = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: daysAgo)!
|
||||||
|
|
||||||
|
moodEntries = PersistenceController.shared.getData(startDate: daysAgo, endDate: Date(), includedDays: [1,2,3,4,5,6,7])
|
||||||
|
}
|
||||||
|
if let moodEntries = moodEntries {
|
||||||
|
for (_, mood) in Mood.allValues.enumerated() {
|
||||||
|
entries.append((mood, moodEntries.filter({
|
||||||
|
Int($0.moodValue) == mood.rawValue
|
||||||
|
}).count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = entries.sorted(by: {
|
||||||
|
$0.0.rawValue > $1.0.rawValue
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
ForEach(entries, id: \.0) { (mood, value) in
|
||||||
|
Text(String(value))
|
||||||
|
.font(.title)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(mood.color)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(String(format: String(localized: "content_view_header_title"), self.backDays))
|
||||||
|
.font(.body)
|
||||||
|
.foregroundColor(Color(UIColor.systemGray))
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
.padding(.top, 2)
|
||||||
|
}
|
||||||
|
.padding([.top])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SmallHeaderView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
SmallRollUpHeaderView(fakeData: true, backDays: 30)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user