# 🤙 Ground Rule
## 🧑🤝🧑 Rules
매일 11시 시작
## ⏰ 스크럼
- 당일 컨디션 공유, 특이사항
- 공부한거 공유
- 오늘 할일 공유
(매일 작성하자!)
## 📝 Commit convention (Karma Style)
feat: 새로운 기능 추가<br>
fix: 버그 수정<br>
docs: 문서 수정<br>
refactor: 코드 리팩토링<br>
style: 코드 포맷팅 (코드 변경이 없는 경우)<br>
test: 테스트 코드 작성<br>
chore: 소스 코드를 건들지 않는 작업(빌드 업무 수정)<br>
merge: 메서드 혹은 타입 이름 from 사용자<br>
asset: 에셋 추가 혹은 에셋 수정<br>
design: 사용자 UI 디자인 변경<br>
build: 빌드 구성 업데이트, 개발 도구 또는 사용자와 관련 없는 기타 변경 사항.
#### 🙌 01/31 스크럼
- 오늘의 컨디션
- 인호: 낫배드 ~ 굿 사이
- 애종: 괜츈
- 하모: 굳
- 특이사항
- 인호: 없음
- 애종: 없음
- 하모: 없음
- 오늘 할 일
- [x] 모델 만들기
- [x] UML 그리기
- [x] 엠쁴엠
- [ ] 개인 공부
- [ ] 공부 후 10시에 만나서 스크럼 + 앞으로 역할 어떻게 나눌지 얘기
- [ ] 네비게이션, 테이블뷰, 버튼들, 코어데이터 관련, 커스텀 그 차트뷰, FileManager
#### 🙌 02/01 스크럼
- 오늘의 컨디션
- 인호: 낫배드 ~ 굿 사이
- 애종: 괜츈
- 하모: 굳
- 특이사항
- 인호: 없음
- 애종: 없음
- 하모: 없음
- 오늘 할 일
- [x] CoreData 구현
- [x] FileManager 구현
- [x] Core Motion, Timer 구현
#### 🙌 02/01 스크럼
- 오늘의 컨디션
- 인호: 낫배드 ~ 굿 사이
- 애종: 괜츈
- 하모: 굳
- 특이사항
- 없음
- 애종: 없음
- 하모: 없음
- 오늘 할 일
- [x] 모델 만들기
- [x] UML 그리기
- [x] 엠쁴엠
- [ ] 개인 공부
- [ ] 공부 후 10시에 만나서 스크럼 + 앞으로 역할 어떻게 나눌지 얘기
- [ ] 네비게이션, 테이블뷰, 버튼들, 코어데이터 관련, 커스텀 그 차트뷰, FileManager
View
Model
https://miro.com/welcomeonboard/UEFtZjE3Tjh6eDJDQ2Ywb2U4Q0JOMGVRcTBSQXNxTmVUYXlKY2RXQlMxdG41eVlSOHpCTk1jSGNMSGhFRG16dXwzNDU4NzY0NTM1NDcyNjUwOTE2fDI=? share_link_id=989212750040
```swift
final class ViewController: UIViewController {
@IBOutlet private weak var xLabel: UILabel!
@IBOutlet private weak var yLabel: UILabel!
@IBOutlet private weak var zLabel: UILabel!
private let coreMotionManager = CMMotionManager()
override func viewDidLoad() {
super.viewDidLoad()
coreMotionManager.accelerometerUpdateInterval = 0.1
coreMotionManager.gyroUpdateInterval = 0.1
print(coreMotionManager.isAccelerometerActive)
}
@IBAction func tapGyroButton(_ sender: Any) {
startGyroscopes()
}
@IBAction func tapGyroStopButton(_ sender: Any) {
coreMotionManager.stopGyroUpdates()
}
@IBAction func tapAccButton(_ sender: Any) {
startAccelerometers()
}
@IBAction func tapAccStopButton(_ sender: Any) {
coreMotionManager.stopAccelerometerUpdates()
}
private func startAccelerometers() {
if !coreMotionManager.isAccelerometerActive && !coreMotionManager.isGyroActive
{
guard let current = OperationQueue.current else { return }
coreMotionManager.startAccelerometerUpdates(to: current) { [weak self] data, error in
guard let self = self,
let data = data?.acceleration,
error == nil
else {
return
}
self.xLabel.text = data.x.description
self.yLabel.text = data.y.description
self.zLabel.text = data.z.description
}
}
}
private func startGyroscopes() {
if !coreMotionManager.isGyroActive && !coreMotionManager.isAccelerometerActive
{
guard let current = OperationQueue.current else { return }
coreMotionManager.startGyroUpdates(to: current) { [weak self]data, error in
guard let self = self,
let data = data?.rotationRate,
error == nil
else {
return
}
self.xLabel.text = data.x.description
self.yLabel.text = data.y.description
self.zLabel.text = data.z.description
}
}
}
}
```
## 그래프 그리기
# **UIBezierPath + CAShapeLayer로 그래프 그리기**
> 진행 순서
>
> 1. CAShapeLayer를 만든다.
> 2. UIBezierPath를 만든다.
> 3. 2에서 만든 UIBezierPath로 내가 만들고 싶은 path만들어줌
> 4. 1에서 만든 CAShapeLayer의 path프로퍼티에 2에서 만든 UIBezierPath넣어준다.
> 5. CAShapeLayer를 addSubLayer해줌.
```swift
import UIKit
class LineGraphView: UIView {
private let valueXLabel = {
let label = UILabel()
label.textColor = .systemRed
label.text = "x: -3362"
return label
}()
private let valueYLabel = {
let label = UILabel()
label.textColor = .systemGreen
label.text = "y: -143"
return label
}()
private let valueZLabel = {
let label = UILabel()
label.textColor = .systemBlue
label.text = "z: 2497"
return label
}()
var values: [CGFloat] = []
var xvalues: [CGFloat] = [10, 20, 40, 0, 30, 10, 20, 30]
var yvalues: [CGFloat] = [50, 70, 10, 60, 90, 50, 10, 10]
init(frame: CGRect, values: [CGFloat]) {
super.init(frame: frame)
self.values = values
configureView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureView() {
self.layer.borderWidth = 2
self.addSubview(valueXLabel)
self.addSubview(valueYLabel)
self.addSubview(valueZLabel)
}
override func draw(_ rect: CGRect) {
super.draw(rect)
drawPath(values, UIColor.systemRed.cgColor)
drawPath(xvalues, UIColor.cyan.cgColor)
drawPath(yvalues, UIColor.systemBlue.cgColor)
}
private func drawPath(_ values: [CGFloat], _ strokeColor: CGColor) {
let graphLayer = CAShapeLayer() // 1
let graphPath = UIBezierPath() // 2
let xOffset: CGFloat = self.frame.width / CGFloat(values.count)
var currentX: CGFloat = 0
let startPosition = CGPoint(x: currentX, y: self.frame.height / 2)
graphPath.move(to: startPosition)
for i in 0..<values.count {
currentX += xOffset
let newPosition = CGPoint(x: currentX, y: self.frame.height / 2 - values[i])
graphPath.addLine(to: newPosition)
}
graphLayer.fillColor = nil
graphLayer.strokeColor = strokeColor
graphLayer.lineWidth = 2
let newPath = graphPath.cgPath
graphLayer.path = newPath // 4
self.layer.addSublayer(graphLayer) // 5
}
}
```
## 핵앰디로 따라와
- 뷰3개 & 뷰모델들
- 첫번째 뷰 & 뷰모델(셀 & 셀 뷰모델)
- 두번째 뷰 & 뷰모델
- 세번째 뷰 & 뷰모델
- 뷰모델에서 매니저들 메서드 갖다 쓰는
- 첫번째 세번째 화면은 코어데이터, 파일매니저, 타이머
- 두번째는 측정기, 타이머
이렇게 쓰일것같아요
- 무슨무슨 매니저들
- 코어데이터 (CoreData)
- 파일매니저 (FileManager)
- 측정기, 타이머 (coreMotion & Timer)
> 프로토콜 위주로 구현
- 그래프 그리는 커스텀 GraphView
- 모델(모델, 엔티티)
- CoreData Entity -> 날짜 시간 모션타입 ID
- JSON에 그 1800개 담는 식
## 리드미
# 📈 GyroData App
**사용자의 기기에서 가속도, 자이로 값을 측정하는 애플리케이션입니다.**
> 프로젝트 기간: 23.1.30 ~ 23.2.5 (7일)
> 팀원: 인호, 애종, 하모
## 📖 목차
1. [팀 소개](#-팀-소개)
2. [기능 소개](#-기능-소개)
3. [적용 기술](#-적용-기술)
4. [폴더 구조](#-폴더-구조)
## 🧑💻 팀 소개
|[인호](https://github.com/inho-98)|[애종](https://github.com/jonghancha)|[하모](https://github.com/lxodud)|
|:-:|:-:|:-:|
|<a href="https://github.com/inho-98"><img width="180px" src="https://user-images.githubusercontent.com/71054048/188081997-a9ac5789-ddd6-4682-abb1-90d2722cf998.jpg"></a>|<a href="https://github.com/inho-98"><img width="180px" src="https://i.imgur.com/7K7rXiB.jpg"></a>|<a href="https://github.com/inho-98"><img width="180px" src="https://i.imgur.com/PcNDqvt.png"></a>|
|`CoreData`, `ListScene`|`FileManager`, `MeasureScene, GraphView`|`CoreMotion`, `DetailScene, GraphView`|
## 📱 기능 소개
#### 1.메인 리스트![]()
|<img src=https://i.imgur.com/aZF0ooW.gif width=200>|<img src=https://i.imgur.com/hjt0Eas.gif width=200>|
|:-:|:-:|
|페이징|삭제|
#### 2.가속도, 자이로 측정화면
|<img src=https://i.imgur.com/VK5R6sC.gif width=200>|<img src=https://i.imgur.com/9O69BAP.gif width=200>|
|:-:|:-:|
|가속도 측정|자이로 측정|
#### 3.측정내용 View, Play화면
|<img src=https://i.imgur.com/IOTl3cd.gif width=200>|
|:-:|
|그래프 Play|
## ⚙️ 적용 기술
#### `MVVM`
- `ViewController`의 코드가 방대해지는 것을 방지하고, 뷰에 보여질 데이터 및 비즈니스 로직의 분리를 명확히 하기 위해 `MVVM`을 적용하였습니다.
- 데이터 및 비즈니스 로직은 `ViewModel`, 뷰의 상태 관리는 `ViewController`, 뷰를 생성하는 로직은 `View`로 분리하였습니다.
#### `CoreData` & `FileManager`
- `CoreData`에는 모델의 생성 일자, 측정 기간, 측정 타입`(Gyo, Acc), UUID`값을 저장합니다.
- `FileManager`에는 측정한 결과값 배열을 저장하고, 모델의 UUID값을 통해 접근합니다.
#### `CoreMotion`
- 사용자 기기의 `Gyro & Acc`값을 측정합니다.
- `Gyro & Acc` 측정 기능의 공통되는 부분을 프로토콜에 구현하였습니다.
#### `GraphView`
- `UIBezierPath`를 이용하여 그래프를 그려줍니다.
- 뷰에 격자를 보여주는 부분과 그래프를 보여주는 부분을 분리하여 구현하였습니다.
## 📂 폴더 구조
```
├── Info.plist
├── MotionDataModel.xcdatamodeld
│ └── MotionDataModel.xcdatamodel
│ └── contents
├── Resource
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ └── Base.lproj
│ └── LaunchScreen.storyboard
└── Source
├── App
│ ├── AppDelegate.swift
│ └── SceneDelegate.swift
├── Error
│ ├── CoreDataError.swift
│ ├── FileManagingError.swift
│ └── MotionManagerError.swift
├── GraphView
│ ├── GraphBackgroundView.swift
│ ├── GraphView.swift
│ └── GraphViewModel.swift
├── Model
│ ├── MotionCoordinate.swift
│ ├── MotionData.swift
│ ├── MotionMeasures.swift
│ └── MotionType.swift
├── Protocol
│ ├── CoreDataManageable.swift
│ ├── FileManagerProtocol.swift
│ └── MotionManagerable.swift
├── Scene
│ ├── DetailScene
│ │ ├── DetailView.swift
│ │ ├── DetailViewController.swift
│ │ └── DetailViewModel.swift
│ ├── ListScene
│ │ ├── DateFormatter +.swift
│ │ ├── MainViewController.swift
│ │ ├── MainViewModel.swift
│ │ ├── MotionCellViewModel.swift
│ │ └── MotionDataCell.swift
│ └── MeasureScene
│ ├── MeasureViewController.swift
│ └── MeasureViewModel.swift
└── Service
├── AccelerometerMotionManager.swift
├── FileHandleManager.swift
├── GyroMotionManager.swift
└── MeasureTimer.swift
```