Skip to content

ryuprogrammer/GoodPostureStudy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

73 Commits
 
 
 
 
 
 

Repository files navigation

姿勢管理アプリ『ポスタ』

1. 概要・コンセプト

2. アプリの機能3つ

3. アプリの動作

タスクの追加方法

タスク内容、アイコンの色、時間を入力するだけで簡単に追加できます。

姿勢の検知

姿勢を検知して、正しい姿勢の時だけタイマーが進みます。

4. ダウンロードリンク

App_Store_Badge_JP

5. 今後追加する機能

今後はStudyPlus(勉強版Twitter)に完了したタスクを登録する機能を実装します! レポート機能を充実させます!

6. MVVMの構成図

・View

・ViewModel

・Model

・Extension

7. シーケンス図

8. フローチャート

AddView -->|追加| AddViewModel HomeView -->|タスク削除| HomeViewModel BodyPoseViewModel -->|姿勢判定| BodyPoseView

HomeViewModel -->|タスク削除| TaskDataModel AddViewModel -->|入力チェック| TaskDataModel PostureModel -->|姿勢取得| BodyPoseViewModel

TaskDataModel -->|更新| id1 PostureModel -->|座標データ取得| id2

9. 工夫したコード/設計

プロジェクトで工夫した設計や、コードを具体的に示してください。 該当コードを示して、どんな工夫をしたのか分かりやすく記載してください。

ポイント1 タスクをタイムチャートで表示

タスクの開始時間と終了時間を可視化するためにタイムチャートを作成しました。

タイムチャート

ZStack {
// 背景の円
Circle()
// 円形の線描写するように指定
.stroke(lineWidth: 50)
.foregroundColor(.gray.opacity(0.15))
// 現在時刻までmask
Circle()
// 0:1=0:360
.trim(from: 0, to: isProgress ? timeDegrees/360 : 0)
.stroke(style: StrokeStyle(lineWidth: 50, lineCap: .butt, lineJoin: .round))
.foregroundColor(.gray.opacity(isShowRemainingTime ? 0.6 : 0))
.rotationEffect(Angle(degrees: 90))
ForEach(tasks) { data in
// 今日のタスクのみ表示
if circularTimeBarViewModel.isEqualToDate(startTime: data.startTime!) {
let color: Color = Color(taskColorName: Color.TaskColorNames(rawValue: data.color!) ?? .blue)
// 進捗を示す円
Circle()
.trim(from: CGFloat(data.startTime!.formattedHourInt())/24 + CGFloat(data.startTime!.formattedMinutesInt())/60,
to: min(isProgress ?
CGFloat(data.endTime!.formattedHourInt())/24 + CGFloat(data.endTime!.formattedMinutesInt())/60: CGFloat(data.startTime!.formattedHourInt())/24 + CGFloat(data.startTime!.formattedMinutesInt())/60,
24))
// 線の端の形状などを指定
.stroke(style: StrokeStyle(lineWidth: 40, lineCap: .butt, lineJoin: .round))
.fill(LinearGradient(gradient: Gradient(colors: [color, color.opacity(0.5)]),
startPoint: .top,
endPoint: .bottom))
.rotationEffect(Angle(degrees: 90))
}
}
ForEach(0..<24) { time in
// 円の区切り線
Rectangle()
.frame(width: 2, height: 300)
.foregroundColor(.white)
.cornerRadius(20)
.rotationEffect(Angle(degrees: Double(time*15)))
// 時計の数字
ZStack {
Text("\(time)")
.position(x: 140, y: -13)
.rotationEffect(Angle(degrees: Double(time*15)+174.5))
}
}
// 時間または残り時間を表示
Text(isShowRemainingTime ? remainingTime : nowTime)
.bold()
.font(.largeTitle)
}
.frame(width: 250, height: 250)
.onTapGesture {
withAnimation {
isShowRemainingTime.toggle()
}
}
.onAppear {
withAnimation(.easeInOut(duration: 1)) {
isProgress = true
}
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
nowTime = Date().formattedTimeString()
remainingTime = Date().getRemainingTimeToday()
// 0:24=0:360
// 0:60=0:15
timeDegrees = Double(Date().formattedHourInt())*15+Double(Date().formattedMinutesInt())/4
}
}
}

ポイント2 過去7日分のデータをChartで表示

CoreDataからタスクデータを取得して、完了済みのタスクの合計時間を過去7日分Chartで表示しました。

過去7日分のレポート

class BarMarkViewModel: ObservableObject {
// Chart用Data
@Published var reportData: [Report] = []
// Calender()のインスタンス生成(グレゴリオ暦を採用)
private let calender = Calendar(identifier: .gregorian)
// Chart用構造体
struct Report: Identifiable {
let id = UUID()
// 日付
let day: String
// 1日の合計勉強時間
let timeCount: Double
}
// startTimeとendTimeから時差を求める
// 開始時間と終了時間から勉強時間(allTime)を算出
func calculateAllTime(startTime: Date, endTime: Date) -> Double {
var allTime: Double = 0.0
if let time = calender.dateComponents([.second], from: startTime, to: endTime).second {
allTime = Double(time)/(60*60)
}
return allTime
}
// taskから今日+過去6日分の勉強データをreportDataに格納
func fetchReport(tasks: FetchedResults<Task>) {
// データの初期化
reportData = []
// 勉強の合計時間
var todayStudyTime: Double = 0
var yesterdayStudyTime: Double = 0
var twoDaysAgoStudyTime: Double = 0
var threeDaysAgoStudyTime: Double = 0
var fourDaysAgoStudyTime: Double = 0
var fiveDaysAgoStudyTime: Double = 0
var sixDaysAgoStudyTime: Double = 0
// 過去6日+今日の年月日を取得
let days = sixDaysAgoDates()
// チャートのx軸(日付)
let today: String = "今日"
let yesterday: String = String(days.first(where: { $1 == 1 })!.key.suffix(5))
let twoDaysAgo: String = String(days.first(where: { $1 == 2 })!.key.suffix(5))
let threeDaysAgo: String = String(days.first(where: { $1 == 3 })!.key.suffix(5))
let fourDaysAgo: String = String(days.first(where: { $1 == 4 })!.key.suffix(5))
let fiveDaysAgo: String = String(days.first(where: { $1 == 5 })!.key.suffix(5))
let sixDaysAgo: String = String(days.first(where: { $1 == 6 })!.key.suffix(5))
for task in tasks {
guard let startTime = task.startTime else { return }
guard let endTime = task.endTime else { return }
// 1つのタスクの合計時間を計算
let studyTime = calculateAllTime(startTime: startTime, endTime: endTime)
// startTimeをString型の年月日に変換
let stringDay = startTime.formattedDateString()
// startTimeの今日を基準とした経過日数
guard let dayElapsed = daySinceToday(stringDay: stringDay) else { return }
// タスクが完了しているか
if task.isDone {
// タスクの経過日数でデータを分ける
switch dayElapsed {
case 0: todayStudyTime += studyTime
case 1: yesterdayStudyTime += studyTime
case 2: twoDaysAgoStudyTime += studyTime
case 3: threeDaysAgoStudyTime += studyTime
case 4: fourDaysAgoStudyTime += studyTime
case 5: fiveDaysAgoStudyTime += studyTime
case 6: sixDaysAgoStudyTime += studyTime
default: break
}
}
}
// 過去6日+今日のチャートデータ
let todaysReport = Report(day: today, timeCount: todayStudyTime)
let yesterdaysReport = Report(day: yesterday, timeCount: yesterdayStudyTime)
let twoDaysAgoReport = Report(day: twoDaysAgo, timeCount: twoDaysAgoStudyTime)
let threeDaysAgoReport = Report(day: threeDaysAgo, timeCount: threeDaysAgoStudyTime)
let fourDaysAgoReport = Report(day: fourDaysAgo, timeCount: fourDaysAgoStudyTime)
let fiveDaysAgoReport = Report(day: fiveDaysAgo, timeCount: fiveDaysAgoStudyTime)
let sixDaysAgoReport = Report(day: sixDaysAgo, timeCount: sixDaysAgoStudyTime)
// チャートデータを追加
reportData.append(contentsOf: [sixDaysAgoReport,
fiveDaysAgoReport,
fourDaysAgoReport,
threeDaysAgoReport,
twoDaysAgoReport,
yesterdaysReport,
todaysReport])
}
// 今日から5日前までの年月日を取得
func sixDaysAgoDates() -> [String: Int] {
let day: Double = 60*60*24
let days: [String: Int] = [
Date().formattedDateString(): 0, // 今日
(Date() - day).formattedDateString(): 1, // 昨日
(Date() - 2*day).formattedDateString(): 2, // 2日前
(Date() - 3*day).formattedDateString(): 3, // 3日前
(Date() - 4*day).formattedDateString(): 4, // 4日前
(Date() - 5*day).formattedDateString(): 5, // 5日前
(Date() - 6*day).formattedDateString(): 6 // 6日前
]
return days
}
// 年月日から経過日数を計算
func daySinceToday(stringDay: String) -> Int? {
let days = sixDaysAgoDates()
// 経過日数
var dayElapsed: Int?
for day in days {
if day.key == stringDay {
dayElapsed = day.value
}
}
return dayElapsed
}
}

10 その他リンク

ホームページ
作成者Twitter

About

正しい姿勢で勉強ができるアプリ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages