
이 코드는 단순히 함수 3개가 아니라 “View의 생명주기(Lifecycle)”를 보여주는 코드야.
🔄 View의 생명주기란?
👉 한 화면(ViewController)이
생성 → 나타남 → 사라짐 → 다시 나타남
이 과정을 반복하는 흐름
📱 이 코드가 의미하는 것
네 코드:
viewDidLoad
viewWillAppear
viewDidAppear
👉 이건 그냥 함수가 아니라
“이 화면이 언제 어떤 상태인지 알려주는 이벤트들”
🎬 쉽게 비유
🎭 무대 비유
- viewDidLoad
👉 무대 준비 (조명, 세트 설치) - viewWillAppear
👉 커튼 올라가기 직전 - viewDidAppear
👉 관객이 실제로 보고 있는 상태
🔥 핵심 흐름
1. viewDidLoad → 처음 생성됨
2. viewWillAppear → 곧 보일 예정
3. viewDidAppear → 이미 보이고 있음
🧠 중요한 개념 (진짜 핵심)
👉 “View는 계속 살아있지 않는다”
- 처음 들어올 때 생성됨
- 사라졌다가 다시 나타남
- 그때마다 특정 함수들이 호출됨
📌 왜 이게 중요하냐
앱 기능은 이 타이밍에 따라 나뉨:
예시 (Night Light 앱 기준)
- viewDidLoad
👉 기본 색 세팅 - viewWillAppear
👉 저장된 밝기 다시 적용 - viewDidAppear
👉 타이머 시작 / 애니메이션 시작
⚠️ 이거 모르면 생기는 문제
초보들이 흔히 하는 실수:
👉 모든 코드를 viewDidLoad에 넣음
결과:
- 화면 다시 들어올 때 값 안 바뀜
- 버그처럼 보임
🔁 더 큰 그림 (전체 생명주기)
사실 더 있음:
viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDidDisappear
👉 나타날 때 / 사라질 때 둘 다 있음
🎯 한 줄 핵심 정리
이 코드는 “화면이 언제 생성되고, 언제 보여지고 있는지 알려주는 타이밍 시스템”




자식들은 자신이 속해있는 부모 뷰를 통해 처리됨



//임의로 배경색 변경
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
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)
}
}
//1초마다 랜덤 배경색
import UIKit
class ViewController: UIViewController {
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// 처음 색 설정
changeBackgroundColor()
// 1초마다 실행
timer = Timer.scheduledTimer(timeInterval: 1.0,
target: self,
selector: #selector(changeBackgroundColor),
userInfo: nil,
repeats: true)
}
@objc func changeBackgroundColor() {
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)
}
}
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var musicSwitch: UISwitch!
var colorChangeTimer: Timer?
var audioPlayer: AVAudioPlayer!
@IBAction func switchDidChange(_ sender: UISwitch) {
if sender.isOn { startTimerAndMusic()
}else { stopTimerAndMusic()
}
}
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()
let alert = UIAlertController(title: "알림", message: "앱을 종료해주세요.", preferredStyle: .alert)
let okAction = UIAlertAction(title: "확인", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
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()
}
}
- 6번 코드
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var musicSwitch: UISwitch!
var colorChangeTimer: Timer?
var fadeTimer: Timer?
var audioPlayer: AVAudioPlayer!
// 현재 밝기
var currentAlpha: CGFloat = 1.0
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)
}
@IBAction func switchDidChange(_ sender: UISwitch) {
if sender.isOn {
startTimerAndMusic()
} else {
stopTimerAndMusic()
}
}
@objc func applicationWillEnterForeground() {
if musicSwitch.isOn {
startTimerAndMusic()
}
}
@objc func handleTapGesture() {
stopTimerAndMusic()
let alert = UIAlertController(title: "알림",
message: "앱을 종료해주세요.",
preferredStyle: .alert)
let okAction = UIAlertAction(title: "확인", style: .default)
alert.addAction(okAction)
present(alert, animated: true)
}
func startTimerAndMusic() {
// 🔴 중복 실행 방지
if colorChangeTimer != nil { return }
// 🎵 음악
if let soundURL = Bundle.main.url(forResource: "bgm", withExtension: "mp3") {
do {
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
audioPlayer.numberOfLoops = -1 // 무한 반복
audioPlayer.volume = 1.0
audioPlayer.play()
} catch {
print("음악 로드 실패: \(error)")
}
}
// 🎨 색상 애니메이션 + 그라데이션
colorChangeTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
// 랜덤 색상 2개를 선택
let color1 = UIColor(red: CGFloat.random(in: 0...1),
green: CGFloat.random(in: 0...1),
blue: CGFloat.random(in: 0...1),
alpha: 1.0)
let color2 = UIColor(red: CGFloat.random(in: 0...1),
green: CGFloat.random(in: 0...1),
blue: CGFloat.random(in: 0...1),
alpha: 1.0)
// 그라데이션 레이어 생성
let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.view.bounds
gradientLayer.colors = [color1.cgColor, color2.cgColor]
// 애니메이션 적용
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
self.view.layer.sublayers?.forEach { $0.removeFromSuperlayer() } // 기존 제거
self.view.layer.insertSublayer(gradientLayer, at: 0)
CATransaction.commit()
}
// 🌙 Fade Out (밝기 점점 낮추기)
currentAlpha = 1.0
fadeTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { _ in
if self.currentAlpha > 0.3 {
self.currentAlpha -= 0.005
self.view.alpha = self.currentAlpha
}
}
}
func stopTimerAndMusic() {
// 타이머 종료
colorChangeTimer?.invalidate()
colorChangeTimer = nil
fadeTimer?.invalidate()
fadeTimer = nil
// 음악 볼륨 천천히 줄이고 정지
if let player = audioPlayer {
player.setVolume(0, fadeDuration: 2.0) // 2초 페이드 아웃
DispatchQueue.main.asyncAfter(deadline: .now() + 2.1) {
player.stop()
}
}
}
}
- 기능을 추가한 코드

label의 텍스트 크기를 자동으로 조절하는 기능
//
// ViewController.swift
// Display
//
// Created by 1 on 2026/03/17.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
@IBAction func displayText(_ sender: UIButton) {
label.text = textField.text
}
override func viewDidLoad() {
super.viewDidLoad()
startScrolling()
}
func startScrolling() {
// 시작 위치: 오른쪽 바깥
label.center.x = view.bounds.width + label.bounds.width / 2
UIView.animate(withDuration: 10, delay: 0, options: [.curveLinear, .repeat], animations: {
// 끝 위치: 왼쪽 바깥
self.label.center.x = -self.label.bounds.width / 2 }, completion: nil)
}
}
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
var scrollTimer: Timer?
var blinkTimer: Timer?
var ledColors: [UIColor] = [.red, .yellow, .white, .orange]
var textToDisplay: String = ""
var displayedText: String = ""
var scrollInterval: TimeInterval = 0.2
override func viewDidLoad() {
super.viewDidLoad()
// 화면 탭 시 LED 색상 랜덤 변경
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
view.addGestureRecognizer(tapGesture)
}
@IBAction func displayText(_ sender: UIButton) {
guard let input = textField.text, !input.isEmpty else { return }
// 1️⃣ 대문자 변환
textToDisplay = input.uppercased()
// 2️⃣ 글자 수에 따른 스크롤 속도 자동 조절
scrollInterval = max(0.05, 5.0 / Double(textToDisplay.count))
// 3️⃣ 스크롤 시작
startScrolling()
// 4️⃣ 20초 후 자동 초기화
DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
if self.textToDisplay == input.uppercased() {
self.textToDisplay = ""
self.displayedText = ""
self.label.text = ""
self.stopTimers()
}
}
}
// MARK: - 스크롤 + LED 점등
func startScrolling() {
stopTimers()
displayedText = textToDisplay + " "
scrollTimer = Timer.scheduledTimer(withTimeInterval: scrollInterval, repeats: true) { _ in
guard !self.displayedText.isEmpty else { return }
// 한 글자씩 이동
let firstChar = self.displayedText.first!
let rest = self.displayedText.dropFirst()
self.displayedText = rest + String(firstChar)
// 글자별 LED 점등
let coloredText = NSMutableAttributedString(string: String(self.displayedText))
for i in 0..<coloredText.length {
let color = self.ledColors.randomElement()!
coloredText.addAttribute(.foregroundColor, value: color, range: NSRange(location: i, length: 1))
}
self.label.attributedText = coloredText
}
// 깜빡임 효과
blinkTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
guard let text = self.label.attributedText else { return }
let mutable = NSMutableAttributedString(attributedString: text)
let length = mutable.length
if length > 0 {
let randomIndex = Int.random(in: 0..<length)
mutable.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(location: randomIndex, length: 1))
self.label.attributedText = mutable
}
}
}
func stopTimers() {
scrollTimer?.invalidate()
scrollTimer = nil
blinkTimer?.invalidate()
blinkTimer = nil
}
@objc func handleTapGesture() {
ledColors.shuffle()
}
}
기능 추가한 전광판 코드

Swift에서 @IBOutlet weak var label: UILabel!처럼 암시적 언래핑(Implicitly Unwrapped Optional, !)을 왜 쓰는지에 대한 이유
1️⃣ 기본 개념
@IBOutlet weak var label: UILabel!
- UILabel! → 암시적 언래핑 옵셔널
- 옵셔널(UILabel?)과 비슷하지만, 컴파일러가 “사용할 때 자동으로 언래핑” 해준다는 의미
- 즉, label.text = "Hello" 처럼 바로 접근 가능, label?.text = "Hello"처럼 안전하게 optional 체크할 필요 없음
2️⃣ 왜 암시적 언래핑을 사용하는가?
2-1. IBOutlet과 스토리보드 연결
- 스토리보드에서 Label, Button, TextField 등 UI 요소를 IBOutlet으로 연결할 때
- 뷰 컨트롤러가 초기화될 때는 label 객체가 아직 생성되지 않음
- 하지만 뷰가 로드(viewDidLoad())되면 스토리보드가 label에 실제 객체를 연결해줌
- 따라서 초기화 시점에는 nil일 수 있으므로 일반 Non-Optional로 선언할 수 없음
- 하지만 뷰가 로드된 후에는 절대 nil이 아니므로 바로 사용 가능 → 안전하게 암시적 언래핑 사용 가능
2-2. Optional(?) 대신 ! 사용 이유
- Optional(UILabel?)으로 선언하면, 접근할 때마다 아래처럼 써야 함:
label?.text = "Hello"
- 하지만 암시적 언래핑(UILabel!)이면, 뷰가 로드된 후 바로 사용 가능:
label.text = "Hello"
- 코드가 더 깔끔하고, 스토리보드와 IBOutlet 구조에서 안전하게 사용 가능
3️⃣ 주의 사항
- 뷰가 아직 로드되기 전에 접근하면 런타임 에러 발생
- 예: label.text = "Hello"를 init()나 viewDidLoad 이전에 접근
- 일반적으로는 viewDidLoad() 이후에는 안전하므로 암시적 언래핑이 권장됨
4️⃣ 요약
구분특징사용 이유
| UILabel! | 암시적 언래핑 옵셔널 | 뷰 로드 이후에 절대 nil이 아니므로, 코드 간결하게 사용 가능 |
| UILabel? | 옵셔널 | 안전하지만 접근할 때 매번 ? 또는 ! 필요 |
| UILabel | 일반 Non-Optional | 초기화 시점에서 반드시 값이 있어야 하므로 스토리보드 연결에서는 사용 불가 |
💡 한 줄 요약:
IBOutlet은 뷰가 로드되기 전에는 nil일 수 있으므로 Optional이어야 하고, 뷰가 로드된 후에는 항상 존재하므로 !로 암시적 언래핑해서 바로 사용하게 만든 것

UIButton 은 자료형처럼 쓰이는 클래스임
func add(x: Int, y: Int) -> Int { return(x+y)
}
print(add(x:10, y:20))
func add(xx x: Int,yy y: Int) -> Int { return(x+y)
}
print(add(xx:20, yy:40))
func add( _ x: Int,yy y: Int) -> Int { return(x+y)
}
print(add(10,yy: 20))

세번째 방식을 가장 많이 씀
Swift 함수의 매개변수 이름(Argument Label)과 내부 이름(Parameter Name) 구분입니다.
1️⃣ 기본 문법
func add(x: Int, y: Int) -> Int {
return x + y
}
print(add(x: 10, y: 20)) // 30
- 여기서 x와 y는 외부 이름 = 내부 이름
- 호출할 때도 add(x:10, y:20)처럼 레이블과 함께 전달해야 함
2️⃣ 외부 이름과 내부 이름 분리
func add(xx x: Int, yy y: Int) -> Int {
return x + y
}
print(add(xx: 20, yy: 40)) // 60
- xx x → 외부 이름 = xx, 내부 이름 = x
- 함수 내부에서는 x로 사용 가능, 호출할 때는 xx: 레이블 사용
- 마찬가지로 yy y → 외부 이름 yy, 내부 이름 y
즉 func add(외부이름 내부이름: 타입) 형태
3️⃣ 외부 이름 생략(_)
func add(_ x: Int, yy y: Int) -> Int {
return x + y
}
print(add(10, yy: 20)) // 30
- _ x → 외부 이름 생략, 호출 시 레이블 없이 바로 값 전달 가능
- yy y → 외부 이름 yy, 호출 시 yy: 사용
- 그래서 호출할 때: add(10, yy: 20)
🔹 요약
선언호출 방식설명
| func add(x: Int, y: Int) | add(x:10, y:20) | 외부 이름 = 내부 이름 |
| func add(xx x: Int, yy y: Int) | add(xx:20, yy:40) | 외부 이름 xx/yy, 내부 이름 x/y |
| func add(_ x: Int, yy y: Int) | add(10, yy:20) | 첫 번째 인자 외부 이름 생략, 두 번째는 yy 사용 |
💡 핵심 포인트
- Swift에서는 함수 인자의 **외부 이름(argument label)**과 **내부 이름(parameter name)**을 구분 가능
- 외부 이름은 호출할 때 쓰이고, 내부 이름은 함수 내부에서 쓰임
- _를 쓰면 외부 이름을 생략 가능
'iOS' 카테고리의 다른 글
| iOS_grade3 - 5주차 (0) | 2026.03.31 |
|---|---|
| iOS_3grade - 4주차 (0) | 2026.03.24 |
| iOS_3grade - 2주차 (0) | 2026.03.10 |
| iOS_3grade - 1주차 (1) | 2026.03.03 |
| iOS_프로젝트 (1) | 2025.12.08 |