iOS앱개발 10주차

2024. 11. 14. 16:48iOS앱개발

생성형 AI를 활용한 BMI 앱 기획서

1. 개요

BMI 계산기 앱은 사용자가 키와 몸무게를 입력하여 본인의 BMI를 계산하고, 건강 상태를 확인할 수 있게 합니다. 추가적으로, 목표 체중을 설정하거나 기록을 관리하여 개인 건강 목표를 달성할 수 있도록 지원합니다.

2. 시장 조사

  • 분석 대상 앱: App Store에서 높은 다운로드 수를 기록한 앱을 중심으로 분석
    • 앱 A: 간단한 BMI 계산과 목표 체중 관리 기능 제공
    • 앱 B: 다양한 BMI 차트와 분석 기능, BMI 기준별 시각화
    • 앱 C: 사용자 BMI 변화 트래킹 기능과 건강 팁 제공
  • 인기 기능 분석
    • BMI 기준에 따른 시각화: BMI 수치와 건강 상태를 한눈에 볼 수 있도록 시각적 표현 (색상, 아이콘 등 사용).
    • 추적 기능: 기록 저장을 통해 개인 BMI 변화를 장기적으로 추적할 수 있는 기능이 사용자들에게 인기.
    • 건강 팁 및 추천 기능: BMI 수치에 따라 개인화된 건강 팁 제공.
    • 다양한 단위 지원: 사용자에게 친숙한 단위 시스템 제공 (cm/m, kg/lb).
    • 체중 목표 설정 및 리마인더 기능: 개인화된 목표를 설정하고 리마인더 알림을 통해 동기 부여.

3. 주요 기능 제안

  1. BMI 계산
    • 키와 몸무게 입력 시 실시간으로 BMI 계산.
    • BMI 계산 결과와 함께 건강 상태(저체중, 정상, 과체중 등) 표시.
  2. 사용자 맞춤 시각화
    • BMI 상태에 따라 배경 색상 변화.
    • 그래픽 차트로 BMI 트렌드를 시각적으로 표시.
  3. 기록 저장 및 트래킹
    • 매일 또는 주간 BMI 기록을 저장하고, 사용자에게 변화를 추적할 수 있는 그래프 제공.
  4. 목표 체중 설정 및 알림
    • 사용자가 목표 체중을 설정하고 이를 위한 BMI 목표를 제안.
    • 설정된 목표를 기반으로 알림을 통해 지속적인 동기 부여.
  5. 건강 정보와 팁 제공
    • BMI에 따른 식단, 운동 팁을 제공해 건강한 생활 습관 형성을 돕는 섹션 포함.

4. 화면 구성 (UI/UX)

  1. 메인 화면
    • BMI 계산기, 키와 몸무게 입력 필드, 계산 버튼, 결과 및 시각화 영역.
  2. 기록 관리 화면
    • 지난 BMI 기록을 보여주는 그래프와 날짜별 기록 리스트.
  3. 목표 설정 화면
    • 목표 체중 설정 및 현재 체중 대비 목표 차이 안내.
  4. 건강 팁 화면
    • BMI 상태에 따른 개인화된 건강 팁과 식단, 운동 정보 제공.
  5. 설정 화면
    • 단위 설정 (cm/m, kg/lb), 알림 설정 및 사용자 프로필 관리.

5. 차별화 포인트

  • 사용자가 지속해서 앱에 방문할 수 있도록 BMI 변화 트래킹 및 목표 달성 리마인더.
  • 간단하고 직관적인 UI로 사용자 접근성 강화.
  • BMI 수치에 따른 다양한 콘텐츠 (건강 팁, 알림 등) 제공.

6. 향후 업데이트

  • 소셜 기능 추가: 목표 달성 시 친구와 공유하거나, 건강 커뮤니티와 연계.
  • 헬스킷 통합: 애플 헬스와 통합하여 자동으로 체중 및 BMI 데이터를 가져오는 기능.
  • 다국어 지원: 다양한 언어로 지원해 글로벌 사용자 확보.

소스코드

import UIKit  // UIKit 라이브러리를 가져옵니다. iOS 앱의 UI를 구성하는 데 필요한 기능들을 포함합니다.

class ViewController: UIViewController {  // UIViewController를 상속받는 ViewController 클래스를 정의합니다.
    
    // Storyboard에서 연결된 키와 몸무게 입력 필드와 결과 레이블입니다.
    @IBOutlet weak var heightTextField: UITextField!  // 키를 입력하는 텍스트 필드
    @IBOutlet weak var weightTextField: UITextField!  // 몸무게를 입력하는 텍스트 필드
    @IBOutlet weak var resultLabel: UILabel!  // BMI 결과를 표시할 레이블
    
    override func viewDidLoad() {  // 화면이 처음 로드될 때 호출되는 메서드입니다.
        super.viewDidLoad()  // 부모 클래스의 viewDidLoad 메서드를 호출해 기본 설정을 완료합니다.
        resultLabel.text = ""  // 앱이 시작될 때 결과 레이블의 텍스트를 빈 값으로 설정합니다.
    }
    
    // BMI 계산 버튼과 연결된 액션 함수입니다.
    @IBAction func calculateBMI(_ sender: UIButton) {  // 버튼을 누르면 호출되는 함수입니다.
        
        // 키와 몸무게 입력을 텍스트 필드에서 가져와 변환합니다.
        guard let heightText = heightTextField.text,  // heightTextField에서 텍스트를 가져옵니다.
              let weightText = weightTextField.text,  // weightTextField에서 텍스트를 가져옵니다.
              let height = Double(heightText),  // 가져온 키 텍스트를 Double(소수)로 변환합니다.
              let weight = Double(weightText) else {  // 가져온 몸무게 텍스트를 Double로 변환합니다.
            
            // 키와 몸무게를 제대로 입력하지 않았을 때 안내 메시지를 표시합니다.
            resultLabel.text = "키와 몸무게를 정확히 입력하세요."  // 결과 레이블에 경고 메시지를 표시합니다.
            resultLabel.textColor = .red  // 경고 메시지의 색상을 빨간색으로 설정합니다.
            return  // 변환 실패 시 함수 종료
        }
        
        // 키를 cm에서 m로 변환하고 BMI를 계산합니다.
        let heightInMeters = height / 100  // cm로 입력된 키를 m 단위로 변환합니다.
        let bmi = weight / (heightInMeters * heightInMeters)  // BMI 계산식에 따라 체중과 키를 사용해 BMI를 계산합니다.
        
        // BMI 결과를 표시하고 상태에 맞게 색상을 설정합니다.
        resultLabel.text = "당신의 BMI는 \(String(format: "%.1f", bmi))입니다."  // BMI 결과를 소수점 첫째 자리까지 표시
        resultLabel.textColor = bmiColor(bmi)  // bmiColor 함수로 계산된 BMI에 따라 색상을 설정합니다.
    }
    
    // BMI 상태에 따른 색상을 반환하는 함수입니다.
    func bmiColor(_ bmi: Double) -> UIColor {  // 계산된 BMI를 받아 해당하는 색상을 반환합니다.
        switch bmi {  // BMI 값에 따라 조건을 분기합니다.
        case ..<18.5:  // BMI가 18.5 미만일 때
            return .blue  // 파란색 반환 (저체중)
        case 18.5..<24.9:  // BMI가 18.5 이상 24.9 미만일 때
            return .green  // 초록색 반환 (정상)
        case 25..<29.9:  // BMI가 25 이상 29.9 미만일 때
            return .yellow  // 노란색 반환 (과체중)
        default:  // BMI가 30 이상일 때
            return .red  // 빨간색 반환 (비만)
        }
    }
}

 

AI 추천 BMI 앱 추가기능

1. BMI 기록 저장 기능

  • 설명: 사용자가 BMI를 계산할 때마다 기록에 추가하여 저장합니다. 이전의 BMI 수치를 확인하거나, 변화 추이를 볼 수 있도록 구현합니다.
  • 구현 방법: BMI 값을 UserDefaults에 저장하고, 기록을 볼 수 있는 간단한 목록 화면을 추가할 수 있습니다.

2. BMI 해석 및 조언

  • 설명: BMI 수치에 따라 간단한 설명과 건강 조언을 제공합니다. 예를 들어, 저체중일 경우 "영양가 있는 음식을 충분히 섭취하세요"와 같은 조언을 표시합니다.
  • 구현 방법: BMI 결과에 따른 조언 텍스트를 레이블로 표시하거나, 추가 화면을 만들어 건강 관련 조언을 더 자세히 보여줍니다.

3. BMI 범위 기준표

  • 설명: 사용자가 각 BMI 범위가 무엇을 의미하는지 쉽게 확인할 수 있도록, BMI 범위를 설명하는 기준표를 제공합니다. 예를 들어, 18.5-24.9는 정상, 25-29.9는 과체중 등으로 표시합니다.
  • 구현 방법: 새로운 화면을 추가하고, 기준표를 보여주는 텍스트나 그래픽 요소를 추가하여 쉽게 참고할 수 있게 합니다.

4. 단위 변환 기능 (cm ↔ ft/in, kg ↔ lb)

  • 설명: 사용자에게 키와 체중을 미터법(cm, kg) 또는 영국식(ft/in, lb)으로 입력할 수 있는 옵션을 제공합니다.
  • 구현 방법: 단위 전환 스위치를 추가하고, 사용자가 원하는 단위를 선택할 수 있게 합니다. 계산할 때는 내부적으로 cm와 kg 단위로 변환한 후 BMI를 계산합니다.

5. 목표 BMI 설정 및 목표 달성 알림

  • 설명: 사용자에게 목표 BMI를 설정하도록 하고, 목표에 얼마나 가까운지, 얼마나 더 해야 하는지를 보여줍니다. 예를 들어, 현재 BMI가 28인 경우, 24.9 이하로 줄이는 것을 목표로 설정할 수 있습니다.
  • 구현 방법: 목표 BMI 입력 필드를 추가하고, 계산 시 현재 BMI와 비교하여 목표 BMI에 도달했는지 여부를 표시합니다.

6. 월별 BMI 변동 그래프

  • 설명: 월별로 BMI 변화를 그래프 형태로 시각화하여 사용자가 체중 변화를 직관적으로 볼 수 있게 합니다.
  • 구현 방법: Swift의 Core Plot 또는 Charts 라이브러리를 사용하여 BMI 변동 그래프를 구현할 수 있습니다. 기록된 BMI 데이터로 그래프를 구성합니다.

7. 건강 관련 콘텐츠 링크

  • 설명: 건강 유지와 체중 관리를 위한 관련 콘텐츠로 연결되는 링크를 제공합니다. 예를 들어, 정부의 건강 사이트나 신뢰할 수 있는 블로그로 연결해, 영양이나 운동 정보에 대한 접근성을 높입니다.
  • 구현 방법: 버튼을 추가해 링크로 연결하거나 웹뷰를 통해 앱 내에서 콘텐츠를 볼 수 있게 합니다.

소스코드에 기능 추가

1. BMI 기록 저장 기능

  • 이 기능은 BMI를 계산할 때마다 값을 UserDefaults에 저장하고, ViewController에 표시되는 간단한 목록 형태로 이전 BMI 기록을 볼 수 있도록 합니다.

2. BMI 기준표 추가 기능

  • BMI 수치에 따른 해석을 보여주는 기준표를 앱에 추가하여, 사용자가 각 범위에 해당하는 설명을 볼 수 있게 합니다.
import UIKit

class ViewController: UIViewController {
    
    // 키와 몸무게 텍스트 필드 및 결과 레이블
    @IBOutlet weak var heightTextField: UITextField!
    @IBOutlet weak var weightTextField: UITextField!
    @IBOutlet weak var resultLabel: UILabel!
    
    // 추가된 UI 요소: 기록 목록 표시 TextView 및 기준표 레이블
    @IBOutlet weak var historyTextView: UITextView!  // BMI 기록을 표시할 텍스트 뷰
    @IBOutlet weak var bmiRangeLabel: UILabel!  // BMI 범위 기준표 설명을 담은 레이블
    
    // UserDefaults 키
    let bmiHistoryKey = "BMIHistory"

    override func viewDidLoad() {
        super.viewDidLoad()
        resultLabel.text = ""  // 초기 결과 레이블 텍스트 비우기
        historyTextView.text = loadBMIHistory()  // 저장된 BMI 기록 불러오기
        bmiRangeLabel.text = "저체중: <18.5\n정상: 18.5–24.9\n과체중: 25–29.9\n비만: ≥30"  // BMI 기준표 설정
    }
    
    // BMI 계산 버튼 액션 함수
    @IBAction func calculateBMI(_ sender: UIButton) {
        guard let heightText = heightTextField.text,
              let weightText = weightTextField.text,
              let height = Double(heightText),
              let weight = Double(weightText) else {
            resultLabel.text = "키와 몸무게를 정확히 입력하세요."
            resultLabel.textColor = .red
            return
        }
        
        let heightInMeters = height / 100
        let bmi = weight / (heightInMeters * heightInMeters)
        
        resultLabel.text = "당신의 BMI는 \(String(format: "%.1f", bmi))입니다."
        resultLabel.textColor = bmiColor(bmi)
        
        saveBMIHistory(bmi)  // BMI 기록 저장 함수 호출
        historyTextView.text = loadBMIHistory()  // 기록을 TextView에 업데이트
    }
    
    // BMI 기록을 저장하는 함수
    func saveBMIHistory(_ bmi: Double) {
        var history = loadBMIHistoryArray()  // 기존 기록을 불러옵니다.
        history.append(bmi)  // 새로운 BMI 기록을 추가합니다.
        
        // 저장 형식으로 문자열 배열을 JSON 형식으로 변환하여 UserDefaults에 저장합니다.
        if let data = try? JSONEncoder().encode(history) {
            UserDefaults.standard.set(data, forKey: bmiHistoryKey)
        }
    }
    
    // 저장된 BMI 기록을 불러와서 배열 형태로 반환하는 함수
    func loadBMIHistoryArray() -> [Double] {
        if let data = UserDefaults.standard.data(forKey: bmiHistoryKey),
           let history = try? JSONDecoder().decode([Double].self, from: data) {
            return history
        }
        return []
    }
    
    // 저장된 BMI 기록을 불러와 문자열로 변환하여 반환
    func loadBMIHistory() -> String {
        let history = loadBMIHistoryArray()  // BMI 기록 배열을 불러옵니다.
        return history.map { "BMI: \($0)" }.joined(separator: "\n")  // BMI 기록을 문자열로 변환
    }
    
    // BMI 상태에 따른 색상 반환
    func bmiColor(_ bmi: Double) -> UIColor {
        switch bmi {
        case ..<18.5:
            return .blue
        case 18.5..<24.9:
            return .green
        case 25..<29.9:
            return .yellow
        default:
            return .red
        }
    }
}

 

outlet과 action이 각각의 label과 button에 잘 연결되어있는지 확인하기

bmi를 계산하여 print로 출력하기

lblResult에 출력되도록 수정하기

소스코드

 let height = Double(txtHeight.text!)!
        let weight = Double(txtWeight.text!)!
        print(height, weight)
        let bmi = weight / (height * height * 0.0001)
        let shortenedBmi = String(format: "%.1f", bmi)
        var body = ""
        if bmi >= 40 { body = "3단계 비만" }
        else if bmi >= 30 && bmi < 40 { body = "2단계 비만" }
        else if bmi >= 25 && bmi < 30 { body = "1단계 비만" }
        else if bmi >= 18.5 && bmi < 25 { body = "정상" }
        else { body = "저체중" }
        print("BMI:\(shortenedBmi), 판정:\(body)")
        lblResult.text = "BMI:\(shortenedBmi), 판정:\(body)"

!가 2개인 이유?

!가 두 번 사용된 이유는 아래와 같습니다:

  1. txtHeight.text!:
    • UITextField의 text 속성은 **옵셔널 타입(String?)**이므로, 이를 강제로 언래핑하여 String 타입으로 변환하기 위해 !가 붙었습니다.
  2. Double(txtHeight.text!)!:
    • 이 부분에서는 텍스트를 Double로 변환하는 Double() 초기화 함수가 사용되었습니다.
    • Double() 함수는 변환에 실패하면 nil을 반환하므로, 변환된 결과가 옵셔널 타입(Double?)입니다.
    • 따라서 Double(txtHeight.text!) 결과를 다시 한 번 강제 언래핑하여 Double 타입으로 변환하기 위해 !를 사용했습니다.

즉, 두 개의 !는 각각 다른 이유로 사용되었습니다:

  • 첫 번째 !는 String?을 String으로 강제 언래핑
  • 두 번째 !는 Double?을 Double로 강제 언래핑

이렇게 강제 언래핑을 사용하면 앱이 종료될 위험이 있기 때문에, 아래와 같이 옵셔널 바인딩(guard let)을 사용하는 것이 더 안전합니다.

 

수정된 소스코드
강제 언래핑을 guard let으로 수정, 결과 표현 시 color 조작 추가

import UIKit

class ViewController: UIViewController {
    
    // IBOutlet으로 연결된 텍스트 필드와 라벨
    @IBOutlet var txtHeight: UITextField!
    @IBOutlet var txtWeight: UITextField!
    @IBOutlet var lblResult: UILabel!
    
    // BMI 계산 버튼을 눌렀을 때 호출되는 함수
    @IBAction func calcBmi(_ sender: UIButton) {
        
        // 키와 몸무게 텍스트 필드의 값이 유효한지 확인
        guard let heightText = txtHeight.text, let weightText = txtWeight.text,
              let height = Double(heightText), let weight = Double(weightText) else {
            // 유효하지 않으면 빨간색 오류 메시지 출력
            lblResult.textColor = .red
            lblResult.text = "키와 몸무게를 정확히 입력하세요."  // 오류 메시지 표시
            return
        }
        
        // 텍스트 필드 값이 유효하면 결과 텍스트 색을 흰색으로 설정
        lblResult.textColor = .white
        
        // BMI 계산 공식: 체중(kg) / (키(m) * 키(m))으로 계산, 여기서는 cm를 m로 변환하기 위해 0.0001을 곱함
        let bmi = weight / (height * height * 0.0001)
        
        // 소수점 한 자리까지 출력하도록 형식 지정
        let shortenedBmi = String(format: "%.1f", bmi)
        
        // 판정 결과를 저장할 변수
        var body = ""
        
        // BMI에 따른 색상 변수 선언 (기본 색은 흰색)
        var color = UIColor.white
        
        // BMI 값에 따라 분류하고, 해당하는 색상과 판정 문자열을 설정
        if bmi >= 40 {
            color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)  // 3단계 비만 (빨간색)
            body = "3단계 비만"
        }
        else if bmi >= 30 && bmi < 40 {
            color = UIColor(displayP3Red: 0.7, green: 0.0, blue: 0.0, alpha: 1.0)  // 2단계 비만 (어두운 빨간색)
            body = "2단계 비만"
        }
        else if bmi >= 25 && bmi < 30 {
            color = UIColor(displayP3Red: 0.4, green: 0.0, blue: 0.0, alpha: 1.0)  // 1단계 비만 (붉은색)
            body = "1단계 비만"
        }
        else if bmi >= 18.5 && bmi < 25 {
            color = UIColor(displayP3Red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)  // 정상 (파란색)
            body = "정상"
        }
        else {
            color = UIColor(displayP3Red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)  // 저체중 (초록색)
            body = "저체중"
        }
        
        // 라벨에 결과 표시
        lblResult.clipsToBounds = true  // 텍스트가 라벨 영역을 넘어가지 않도록 설정
        lblResult.layer.cornerRadius = 10  // 라벨에 둥근 모서리 적용
        lblResult.backgroundColor = color  // BMI에 따른 배경색 설정
        lblResult.text = "BMI:\(shortenedBmi), 판정:\(body)"  // 결과 출력
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 추가적인 초기 설정이 필요하다면 여기에 추가
    }
}

생성형 AI를 통해 판정 결과 별 배경색을 바꾸는 방법

@IBAction func calcBmi(_ sender: UIButton) {
    // 키와 체중 입력이 비어 있는지 확인
    if txtHeight.text == "" || txtWeight.text == "" {
        print("Input error")
        lblResult.text = "키와 체중을 입력하세요!"
        return
    } else {
        // 텍스트 필드에서 입력받은 값을 Double로 변환
        let height = Double(txtHeight.text!)!
        let weight = Double(txtWeight.text!)!
        print(height, weight)
        
        // BMI 계산
        let bmi = weight / (height * height * 0.0001)  // 키를 cm에서 m로 변환하려면 0.0001을 곱함
        let shortenedBmi = String(format: "%.1f", bmi)
        
        // BMI에 따른 판정
        var body = ""
        var color = UIColor.white  // 기본 색상은 흰색
        
        // BMI 값에 따라 판정 및 색상 설정
        if bmi >= 40 {
            body = "3단계 비만"
            color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)  // 빨간색
        } else if bmi >= 30 && bmi < 40 {
            body = "2단계 비만"
            color = UIColor(displayP3Red: 0.7, green: 0.0, blue: 0.0, alpha: 1.0)  // 어두운 빨간색
        } else if bmi >= 25 && bmi < 30 {
            body = "1단계 비만"
            color = UIColor(displayP3Red: 0.4, green: 0.0, blue: 0.0, alpha: 1.0)  // 붉은색
        } else if bmi >= 18.5 && bmi < 25 {
            body = "정상"
            color = UIColor(displayP3Red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)  // 파란색
        } else {
            body = "저체중"
            color = UIColor(displayP3Red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)  // 초록색
        }
        
        // BMI와 판정을 라벨에 출력
        print("BMI:\(shortenedBmi), 판정:\(body)")
        lblResult.text = "BMI:\(shortenedBmi), 판정:\(body)"
        
        // 라벨의 배경 색상 변경
        lblResult.backgroundColor = color
    }
}

Tab Bar Controller 만들기 

새로운 view Controller를 생성하여 Tab Bar control을 연결해 새로운 view를 생성가능하다

mp4 파일을 업로드할때는 add to target 부분에 내가 업로드하고자 할 파일을 반드시 체크해야한다.

출저: 스마일한의 스위프트 기초

 

'iOS앱개발' 카테고리의 다른 글

iOS 앱개발 13주  (1) 2024.12.05
iOS앱개발 12주차  (0) 2024.11.28
iOS앱개발 9주차  (2) 2024.11.07
iOS앱개발 8주차  (3) 2024.10.31
iOS 앱개발 7주차  (1) 2024.10.17