Jihyeonnn
송지현
Jihyeonnn
전체 방문자
오늘
어제
  • Jihyeon Song (29)
    • C++ (0)
    • C (0)
    • Linux (0)
    • iOS (23)
    • JAVA (0)
    • Python (1)
    • GitHub (0)
    • 개인 프로젝트 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 기초
  • generic
  • 코딩
  • iOS개발
  • 프로그래밍
  • ios
  • Mac
  • optionalchaining
  • swift
  • UI
  • Mac사용법
  • iOS 개발
  • 개발자
  • 파이썬 #python #개발 #기초
  • array
  • 개발
  • 스위프트
  • IT

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Jihyeonnn

송지현

Swift로 무드등 만들기
iOS

Swift로 무드등 만들기

2025. 3. 19. 16:29

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
    'iOS' 카테고리의 다른 글
    • Swift의 주요 문법 복습
    • Swift로 전광판 만들기
    • Swift 문법 정리
    • Swift 재활하기
    Jihyeonnn
    Jihyeonnn

    티스토리툴바