본문 바로가기
Apple Developer Academy

[버닝버디] 링차트 기능 개발

by Bokoo14 2024. 1. 21.

현재 유지보수 중인 버닝버디 앱의 추가 기능을 개발해야 한다.

 

[해야 할 일]

1인 기능 개발 (1월 20일 ~ ASAP)

  • 이슈 생성
  • 컨벤션 확인
  • 린트 다운받기
  • 린트에 맞게 코드 수정
  • 추가 뷰 그리기 (차트)
  • background, foreground일때 헬스 데이터 관리
  • 피그마의 새로바뀐 로직에 맞게 1인 기능 잘 작동하게 바꾸기

 

[차트 기능 구현]

새로 업데이트된 차트에는 차트 1개짜리 있어서 쉽게 구현 가능할듯

-> 버닝버디의 앱은 iOS+17부터 있는 차트 기능을 사용하면 좋을듯

-> 앱의 생애주기 업데이트에 따라, 핼스 데이터를 실시간으로 받아오기 혹은 잠깐 멈춰있다가 앱이 켜지면 업데이트해줄지 중요 (생애주기가 업데이트가 가장 관권!), 앱 꺼졌을때, 켜졌을때 운동 데이터 관리해줘야 함!

foreground일떄는 계속 운동 데이터를 업데이트 해줘야 함

운동 데이터: 스토리지같은 것을 사용하여 운동 데이터 저장하기


도넛 차트 구현

iOS 17부터 도넛 차트를 쉽게 구현할 수 있다. 

https://developer.apple.com/videos/play/wwdc2023/10037/

 

Explore pie charts and interactivity in Swift Charts - WWDC23 - Videos - Apple Developer

Swift Charts has come full circle: Get ready to bake up pie and donut charts in your app with the latest improvements to the framework...

developer.apple.com

하지만, 우리 앱의 Minimum Deployments를 이 기능때문에 올릴 수는 없기 때문에, 해당 Charts에서 제공해주는 기능을 사용할 수 없다.

 

그래서 여러 방법을 찾아본 결과, SwiftUI로 구현된 앱에 UIKit을 추가해서 사용하기로 결정했다. 

 

테스트 코드

UIKitTest.swift

//
//  UIKitTest.swift
//  ChartTest
//
//  Created by Bokyung on 1/21/24.
//

import SwiftUI


struct TestView: UIViewRepresentable {
    var value: CGFloat // value 매개변수 추가
    
    func makeUIView(context: Context) -> UIView {
        let customView = CustomView()
        customView.value = self.value // value 값 전달
        return customView
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        guard let customView = uiView as? CustomView else { return }
        customView.value = value // value 값 업데이트
        customView.setNeedsDisplay() // 변경사항을 반영하기 위해 화면을 다시 그립니다.
    }

}


class CustomView: UIView {
    var value: CGFloat = 0.0 // 데이터가 채워진 비율 (0.0부터 1.0까지)
    
    override init(frame: CGRect) {
         super.init(frame: frame)
         backgroundColor = UIColor.clear // 배경색 지정
     }
     
     required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
         backgroundColor = UIColor.clear // 배경색 지정
     }
     
    override func draw(_ rect: CGRect) {
//        var value: CGFloat = 0.3 // 데이터가 채워진 비율 (0.0부터 1.0까지)
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius: CGFloat = min(rect.width, rect.height) / 2 - 20
        let grayColor = UIColor(named: "customGray") ?? UIColor.systemGray
        let redColor = UIColor(named: "customRed") ?? UIColor.red
        
        // 회색 선 경로
        let grayPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        grayColor.setFill()
        grayPath.fill()
        
        // 가운데 뚫어주기
        let innerPath = UIBezierPath(arcCenter: center, radius: radius - 28, startAngle: 0, endAngle: 2 * .pi, clockwise: true)
        grayColor.setFill()
        innerPath.fill(with: .clear, alpha: 1.0) // 가운데를 뻥 뚫어줌
        
        // 빨간 선 경로
        let redPath = UIBezierPath(arcCenter: center, radius: radius - 14, startAngle: -.pi / 2, endAngle: -.pi / 2 + 2 * .pi * value, clockwise: true)
        redColor.setStroke()
        redPath.lineWidth = 28
        redPath.lineCapStyle = .round // 끝 부분을 뭉툭하게 만듦
        redPath.stroke()
    }
}



struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        TestView(value: 0.3)
            .frame(width: 249, height: 249)
    }
}

 

 

ContentView.swift

value값을 통해 도넛차트의 붉은 부분이 표시

//
//  ContentView.swift
//  ChartTest
//
//  Created by Bokyung on 1/21/24.
//

import SwiftUI

struct ContentView: View {
    @State private var value: CGFloat = 0.0 // @State 변수 추가
    
    var body: some View {
        VStack {
            TestView(value: value) // TestView로 value 전달
                .frame(width: 249, height: 249)
            
            Button("Change Value") {
                if value < 1.0 {
                    value += 0.1 // 버튼을 눌러 value 값을 변경
                } else {
                    value = 0
                }
                
            }
        }
    }
}