1. 앱 개요
- 앱 이름: MoodLight
- 목적: 사용자가 다양한 무드등 색상과 밝기를 조절하여 개인의 기분 및 환경에 맞게 조명 설정을 할 수 있도록 돕는 앱.
2. 주요 기능
- 색상 선택 기능
- 색상 팔레트 제공
- 색상 믹싱 기능 (RGB 슬라이더)
- 밝기 조절 - 슬라이더를 통한 밝기 조절
- 프리셋 밝기 설정 (예: 낮, 저녁, 밤) - 타이머 기능
- 특정 시간에 자동으로 켜지거나 꺼지게 설정 - 음악 연동
- 음악 재생 시 조명이 변하는 기능 (비트에 맞춰 색상 변화) - 테마 저장
- 사용자 맞춤형 테마 저장 및 불러오기 기능 - 공유 기능
- 설정한 조명 테마를 소셜 미디어로 공유
3. 사용자 인터페이스 (UI) 디자인
- 홈 화면: 색상 선택 및 밝기 조절 슬라이더, 최근 설정된 테마 표시
- 설정 화면: 타이머 및 음악 연동 옵션
- 테마 저장 화면: 저장된 테마 목록 및 삭제 기능
4. 기술 스택
- 프로그래밍 언어: Swift
- 프레임워크: UIKit, CoreBluetooth (무드등과의 연결을 위한)
- 데이터 저장: CoreData 또는 UserDefaults (사용자 설정 저장)
5. 마케팅 전략
- 타겟 사용자: 젊은 성인 및 홈 인테리어 관심자
- 프로모션 계획: SNS 캠페인, 인플루언서 마케팅
- 앱 출시: 앱스토어에서 무료 다운로드 후 인앱 구매 모델 적용
6. 일정 계획
- 1단계: 시장 조사 및 기획 (1개월)
- 2단계: 디자인 및 프로토타입 개발 (2개월)
- 3단계: 개발 및 내부 테스트 (3개월)
- 4단계: 베타 테스트 및 피드백 반영 (1개월)
- 5단계: 정식 출시 (1개월)
7. 예산 계획
- 개발 비용: 인력 비용, 소프트웨어 라이센스
- 마케팅 비용: 광고비, 프로모션 비용
Mimimum Deployments 는 앱이 실행될 수 있는 최소 iOS 버전을 말한다.
Bundle Identifier 에서는 내 앱이라는 흔적을 남길 수 있다. (다른 앱과 같으면 안 된다)
Deployment Info 의 Upside Down은 180도 회전, Landscape Left와 Right은 각각 좌우로 90도 회전을 나타낸다.
(iOS에서는 보통 Upside Down을 구현하지 않는다!)
Life Cycle의 상태는 5개의 시간적 시점이 있다.
import UIKit
class ViewController: UIViewController {
// 뷰가 메모리에 로드될 때 호출되는 메서드
override func viewDidLoad() {
super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 동작을 수행
print("viewDidLoad") // 뷰가 로드되었음을 나타내는 메시지 출력
}
// 뷰가 화면에 나타나기 직전에 호출되는 메서드
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) // 부모 클래스의 viewWillAppear 메서드를 호출하여 기본 동작을 수행
print("viewWillAppear") // 뷰가 나타나기 직전임을 나타내는 메시지 출력
}
// 뷰가 화면에 완전히 나타난 후 호출되는 메서드
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) // 부모 클래스의 viewDidAppear 메서드를 호출하여 기본 동작을 수행
print("viewDidAppear") // 뷰가 완전히 나타났음을 나타내는 메시지 출력
}
}
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 애플리케이션이 시작된 후 사용자 정의를 위한 오버라이드 포인트.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// 새로운 씬 세션이 생성될 때 호출됩니다.
// 이 메서드를 사용하여 새 씬을 생성할 구성을 선택합니다.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// 사용자가 씬 세션을 버릴 때 호출됩니다.
// 애플리케이션이 실행되지 않을 때 버려진 세션이 있는 경우, 이 메서드는 application:didFinishLaunchingWithOptions 다음에 호출됩니다.
// 버려진 씬에 특정한 리소스를 해제하는 데 이 메서드를 사용합니다. 이 씬들은 다시 돌아오지 않습니다.
}
}
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow? // 앱의 주요 윈도우를 나타내는 변수
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// UIWindowScene에 UIWindow를 구성하고 연결하는 메서드
// 스토리보드를 사용하는 경우, window 속성이 자동으로 초기화됨
guard let _ = (scene as? UIWindowScene) else { return } // UIWindowScene으로 변환 가능한지 확인
}
func sceneDidDisconnect(_ scene: UIScene) {
// 씬이 시스템에 의해 해제될 때 호출
// 씬이 백그라운드로 들어가거나 세션이 버려질 때 발생
// 다음 연결 시 재생성할 수 있는 씬 관련 리소스를 해제
}
func sceneDidBecomeActive(_ scene: UIScene) {
// 씬이 비활성 상태에서 활성 상태로 전환될 때 호출
// 씬이 비활성 상태일 때 일시 중지된 작업을 재시작
}
func sceneWillResignActive(_ scene: UIScene) {
// 씬이 활성 상태에서 비활성 상태로 전환될 때 호출
// 예: 전화 통화와 같은 일시적인 중단으로 인해 발생
}
func sceneWillEnterForeground(_ scene: UIScene) {
// 씬이 백그라운드에서 포그라운드로 전환될 때 호출
// 백그라운드 진입 시의 변경 사항을 되돌리는 작업 수행
}
func sceneDidEnterBackground(_ scene: UIScene) {
// 씬이 포그라운드에서 백그라운드로 전환될 때 호출
// 데이터 저장, 공유 리소스 해제 및 현재 상태 정보를 저장하는 작업 수행
}
}
- 모든 View는 UIKit의 UIView 클래스의 자식 클래스이다.
랜덤으로 여러 색이 나오는 무드등 소스이다.
import UIKit
class ViewController: UIViewController {
var colorChangeTimer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
colorChangeTimer =. Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
let red = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
colorChangeTimer?.invalidate()
colorChangeTimer = nil
}
}
음악까지 같이 재생되는 기능을 넣었다
일정 시간 후에 앱을 강제로 종료하는 것은 Apple의 가이드라인에 위배되는 행위이기 때문에 불가능하다.
대신 사용자에게 앱을 종료하라는 메시지를 보여 준 후 확인 버튼을 누르면 주요 기능을 중지시키는 것은 가능하다.
import UIKit
import AVFoundation // 오디오를 다루기 위한 프레임워크 추가
class ViewController: UIViewController {
var colorChangeTimer: Timer?
var audioPlayer: AVAudioPlayer!
override func viewDidLoad() {
super.viewDidLoad()
// 배경 음악 재생 설정
if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
audioPlayer.play()
} catch {
print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
}
}
// 1초마다 반복되는 타이머 생성
colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
// 랜덤한 빨간색, 초록색, 파란색 값을 생성
let red = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// ViewController가 사라질 때 타이머를 중지하고 nil로 설정하여 메모리 누수를 방지
colorChangeTimer?.invalidate()
colorChangeTimer = nil
// 음악 재생 중지
audioPlayer.stop()
}
}
권장하는 방법대로 기능을 구현해 보았다.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var colorChangeTimer: Timer?
var audioPlayer: AVAudioPlayer!
@IBOutlet weak var musicSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
view.addGestureRecognizer(tapGesture)
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
@objc func applicationWillEnterForeground() {
startTimerAndMusic()
}
@objc func handleTapGesture() {
stopTimerAndMusic()
colorChangeTimer = nil
audioPlayer = nil
let alert = UIAlertController(title: "알림", message: "앱을 종료해주세요.", preferredStyle: .alert)
let okAction = UIAlertAction(title: "확인", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
@IBAction func switchDidChange(_ sender: UISwitch) {
if sender.isOn {
startTimerAndMusic()
}else {
stopTimerAndMusic()
}
}
func startTimerAndMusic() {
// 배경 음악 재생 설정
if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
audioPlayer.play()
} catch {
print("음악 파일 로드 또는 재생 중 에러 발생: \(error)")
}
}
// 1초마다 반복되는 타이머 생성
colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
// 랜덤한 빨간색, 초록색, 파란색 값을 생성
let red = CGFloat(arc4random_uniform(256)) / 255.0
let green = CGFloat(arc4random_uniform(256)) / 255.0
let blue = CGFloat(arc4random_uniform(256)) / 255.0
// 생성된 랜덤 색상을 사용하여 ViewController의 배경색 설정
self.view.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
}
func stopTimerAndMusic() {
// 타이머와 음악 재생 중지
colorChangeTimer?.invalidate()
colorChangeTimer = nil
audioPlayer.stop()
}
}
어떤 UI 요소를 화면 정중앙에 위치하게 하려면 맨 아래 두 개의 항목을 체크하고 0으로 설정하면 된다.
현재 화면인데, 왼쪽이나 오른쪽으로 90도 기울이면 스위치가 사라지는 문제가 있다. 이를 해결하기 위해 Auto Layout을 설정해 주겠다.
스위치를 클릭하고 아래 설정대로 해 주면 문제가 해결된다.
Auto Layout 방법들의 장단점이다. ChatGPT를 사용하여 작성하였다.
'iOS' 카테고리의 다른 글
Swift의 주요 문법 복습 (0) | 2025.03.26 |
---|---|
Swift로 전광판 만들기 (0) | 2025.03.19 |
Swift 문법 정리 (0) | 2025.03.18 |
Swift 재활하기 (0) | 2025.03.05 |
Swift - 클로저(Closure) (0) | 2024.12.11 |