지난 시간 복습
전체 View 밑에 Safe Area 는 디자인을 할 수 있는 공간

object 추가 단축키: shift cmd L
attribute inspector를 사용하여 속정 지정
- background 속성은 View에서 지정

connections inspector, 전체를 확인하기 위해서 View Controller 선택
Do it! 스위프트로 아이폰 앱 만들기 입문
02 Hello World 앱 만들며 Xcode에 완벽 적응하기 : 2장은 이전 수업시간에 했던 기초 내용의 반복임.
03 원하는 이미지 화면에 출력하기 - 이미지 뷰 : 이미지 확대/축소, 이미지 변경
- gpt가 해준 2장 코드 설명
//
// ViewController.swift
// ImageView
//
// Created by Ho-Jeong Song on 2021/11/23.
//
import UIKit // UIKit 프레임워크를 불러옴 (iOS UI 구성요소 사용을 위해 필요)
class ViewController: UIViewController { // UIViewController를 상속받은 클래스
// 이미지 확대/축소 상태를 저장할 변수
var isZoom = false // false면 기본 크기, true면 확대 상태
// 이미지 리소스를 저장할 변수
var imgOn: UIImage? // 전구 켜짐 이미지
var imgOff: UIImage? // 전구 꺼짐 이미지
// 스토리보드에 연결된 아웃렛(IBOutlet)
@IBOutlet var imgView: UIImageView! // 화면에 전구 이미지를 보여주는 이미지뷰
@IBOutlet var btnResize: UIButton! // 확대/축소를 위한 버튼
// 뷰가 메모리에 로드된 직후 호출되는 함수 (앱 실행 시 1회 호출)
override func viewDidLoad() {
super.viewDidLoad()
// 추가 초기 설정을 수행하는 부분
// 이미지 리소스를 불러옴 (Assets.xcassets 또는 프로젝트에 포함된 이미지 파일)
imgOn = UIImage(named: "lamp_on.png")
imgOff = UIImage(named: "lamp_off.png")
// 앱 실행 시 기본으로 전구 켜짐 이미지를 표시
imgView.image = imgOn
}
// "확대/축소" 버튼이 눌렸을 때 호출되는 액션 함수
@IBAction func btnResizeImage(_ sender: UIButton) {
let scale: CGFloat = 2.0 // 확대/축소 비율
var newWidth: CGFloat, newHeight: CGFloat // 변경된 크기를 담을 변수
if (isZoom) { // 현재 확대 상태일 때 (true)
// 이미지를 원래 크기로 축소
newWidth = imgView.frame.width / scale
newHeight = imgView.frame.height / scale
// 버튼의 제목을 "확대"로 변경 (다음 동작이 확대가 됨을 의미)
btnResize.setTitle("확대", for: .normal)
}
else { // 현재 축소 상태일 때 (false)
// 이미지를 2배로 확대
newWidth = imgView.frame.width * scale
newHeight = imgView.frame.height * scale
// 버튼의 제목을 "축소"로 변경 (다음 동작이 축소가 됨을 의미)
btnResize.setTitle("축소", for: .normal)
}
// 이미지뷰의 크기를 새 크기로 설정 (이미지 자체가 아닌 프레임을 조정)
imgView.frame.size = CGSize(width: newWidth, height: newHeight)
// 상태를 반전시킴 (true <-> false)
isZoom = !isZoom
}
// 스위치가 켜짐/꺼짐으로 바뀔 때 호출되는 액션 함수
@IBAction func switchImageOnOff(_ sender: UISwitch) {
if sender.isOn {
// 스위치가 켜진 경우: 전구 켜진 이미지로 변경
imgView.image = imgOn
} else {
// 스위치가 꺼진 경우: 전구 꺼진 이미지로 변경
imgView.image = imgOff
}
}
}
- refactoring code
//
// ViewController.swift
// ImageView
//
// Created by Ho-Jeong Song on 2021/11/23.
// Refactored by ChatGPT on 2025/11/03.
//
import UIKit
final class ViewController: UIViewController {
// MARK: - Properties
private var isZoomed = false
private let scaleFactor: CGFloat = 2.0
private var lampOnImage: UIImage?
private var lampOffImage: UIImage?
// MARK: - Outlets
@IBOutlet private weak var imageView: UIImageView!
@IBOutlet private weak var resizeButton: UIButton!
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupImages()
setupInitialView()
}
// MARK: - Setup Methods
private func setupImages() {
lampOnImage = UIImage(named: "lamp_on.png")
lampOffImage = UIImage(named: "lamp_off.png")
}
private func setupInitialView() {
imageView.image = lampOnImage
resizeButton.setTitle("확대", for: .normal)
}
// MARK: - Actions
@IBAction private func didTapResizeButton(_ sender: UIButton) {
toggleImageSize()
}
@IBAction private func didChangeSwitchState(_ sender: UISwitch) {
imageView.image = sender.isOn ? lampOnImage : lampOffImage
}
// MARK: - Helper Methods
private func toggleImageSize() {
let newScale: CGFloat = isZoomed ? 1 / scaleFactor : scaleFactor
let newWidth = imageView.frame.width * newScale
let newHeight = imageView.frame.height * newScale
imageView.frame.size = CGSize(width: newWidth, height: newHeight)
resizeButton.setTitle(isZoomed ? "확대" : "축소", for: .normal)
isZoomed.toggle()
}
}
개선 항목 설명
✅ final class 사용 상속이 필요 없는 ViewController는 final로 선언하여 성능 최적화
✅ private 접근 제어자 사용 외부에서 접근할 필요 없는 속성/메서드는 private으로 보호
✅ MARK 주석 구분 Properties, Lifecycle, Actions 등을 구분해 구조를 명확히 함
✅ isZoom → isZoomed 변수명 변경 Swift naming convention: 상태를 나타낼 때 과거분사 사용 (isEnabled, isHidden 등)
✅ setupImages() / setupInitialView() 분리 초기 설정 코드를 역할별로 분리해 가독성 향상
✅ 삼항 연산자 사용 (sender.isOn ? ... : ...) 조건문 단순화
✅ toggle() 메서드 사용 불리언 값 반전 시 isZoomed = !isZoomed → isZoomed.toggle() 로 간결하게 변경
✅ 매직 넘버 제거 scaleFactor 상수로 정의하여 의미를 명확히 함
04 데이트 피커 사용해 날짜 선택하기 : UIDatePicker(날짜 선택기) 와 UILabel(현재 시간 표시) 를 이용해,
현재 시간과 사용자가 선택한 시간을 화면에 표시하는 iOS 앱
//
// ViewController.swift
// DatePicker
//
// Created by Ho-Jeong Song on 2021/11/24.
//
import UIKit // UIKit 프레임워크를 불러옴 (UI 요소를 사용하기 위해 필요)
class ViewController: UIViewController {
// MARK: - Properties
// 타이머가 호출할 함수(메서드)를 지정하기 위한 셀렉터(Selector)
let timeSelector: Selector = #selector(ViewController.updateTime)
// 타이머가 작동하는 간격 (초 단위) — 1초마다 반복
let interval = 1.0
// 카운터 (테스트용 변수로, 코드에서 현재는 사용되지 않음)
var count = 0
// MARK: - Outlets
// 현재 시간을 표시할 UILabel (Storyboard와 연결)
@IBOutlet var lblCurrentTime: UILabel!
// 사용자가 DatePicker에서 선택한 시간을 표시할 UILabel
@IBOutlet var lblPickerTime: UILabel!
// MARK: - Lifecycle
// 뷰가 메모리에 로드된 직후 한 번만 호출됨
override func viewDidLoad() {
super.viewDidLoad()
// 화면이 처음 로드될 때 수행되는 초기 설정 코드
// 타이머 설정:
// 1초(interval)마다 timeSelector에 지정된 함수(updateTime)를 호출함.
Timer.scheduledTimer(
timeInterval: interval, // 반복 간격: 1초
target: self, // 타이머가 호출할 대상: 현재 뷰 컨트롤러
selector: timeSelector, // 호출할 메서드: updateTime()
userInfo: nil, // 추가 정보 전달 없음
repeats: true // 반복 실행 여부: true → 계속 반복
)
}
// MARK: - Actions
// 사용자가 DatePicker에서 날짜/시간을 변경할 때 호출되는 함수
@IBAction func changeDatePicker(_ sender: UIDatePicker) {
let datePickerView = sender // 선택된 UIDatePicker 인스턴스 가져오기
// 날짜와 시간을 보기 좋게 문자열로 바꾸기 위한 포맷터
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm EEE"
// 예시 결과: "2025-11-03 14:25 Mon"
// 선택된 날짜를 포맷에 맞게 문자열로 변환하여 라벨에 표시
lblPickerTime.text = "선택시간: " + formatter.string(from: datePickerView.date)
}
// MARK: - Timer Callback
// 타이머에 의해 1초마다 호출되는 함수
@objc func updateTime() {
// 아래 주석 처리된 코드는 count 변수를 단순히 증가시키는 테스트용 코드였음
// lblCurrentTime.text = String(count)
// count = count + 1
// 현재 시각(Date) 객체를 생성
let date = NSDate()
// 날짜를 문자열로 보기 좋게 변환하기 위한 DateFormatter 생성
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
// 예시 결과: "2025-11-03 14:25:43 Mon"
// 현재 시간을 문자열로 변환하여 라벨에 표시
lblCurrentTime.text = "현재시간: " + formatter.string(from: date as Date)
}
}
05 피커 뷰 사용해 원하는 항목 선택하기: UIPickerView(픽커뷰) 를 사용해서 여러 이미지 중 하나를 선택하고,
그 이미지 이름과 실제 이미지를 화면에 표시하는 iOS 예제
//
// ViewController.swift
// PickerView
//
// Created by Ho-Jeong Song on 2021/11/25.
//
import UIKit // UIKit 프레임워크 (UI 구성요소 사용을 위해 필요)
// UIViewController를 상속받고, UIPickerView의 Delegate & DataSource 프로토콜을 채택
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
// MARK: - Constants
// 사용할 이미지의 개수 (배열 크기)
let MAX_ARRAY_NUM = 10
// PickerView의 열(column) 수 — 현재는 1열짜리 픽커뷰
let PICKER_VIEW_COLUMN = 1
// PickerView 각 행(row)의 높이 (픽커 안에서 이미지 표시 높이)
let PICKER_VIEW_HEIGHT: CGFloat = 80
// MARK: - Variables
// 실제 UIImage 객체들을 저장할 배열
var imageArray = [UIImage?]()
// 이미지 파일 이름들을 저장하는 배열
var imageFileName = [
"1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg",
"6.jpg", "7.jpg", "8.jpg", "9.jpg", "10.jpg"
]
// MARK: - Outlets (Storyboard 연결 요소)
// 이미지 선택을 위한 UIPickerView
@IBOutlet var pickerImage: UIPickerView!
// 선택된 이미지 파일 이름을 표시할 UILabel
@IBOutlet var lblImageFileName: UILabel!
// 선택된 이미지를 크게 보여줄 UIImageView
@IBOutlet var imageView: UIImageView!
// MARK: - Lifecycle
// 뷰가 메모리에 로드된 직후 한 번 실행됨
override func viewDidLoad() {
super.viewDidLoad()
// 추가 초기 설정 코드
// 이미지 파일 이름 배열을 순회하며 UIImage 객체를 생성해 imageArray에 추가
for i in 0 ..< MAX_ARRAY_NUM {
let image = UIImage(named: imageFileName[i])
imageArray.append(image)
}
// 앱 실행 시 초기화: 첫 번째 이미지 파일명과 이미지를 표시
lblImageFileName.text = imageFileName[0]
imageView.image = imageArray[0]
}
// MARK: - UIPickerViewDataSource (필수 구현 메서드)
// PickerView의 열(column) 개수를 반환
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return PICKER_VIEW_COLUMN
}
// 각 행(row)의 높이를 설정
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return PICKER_VIEW_HEIGHT
}
// PickerView의 각 열에 표시될 행(row)의 개수를 반환
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return imageFileName.count
}
// MARK: - UIPickerViewDelegate (행의 내용 설정)
/*
// 텍스트로 행을 표시하고 싶을 때 사용하는 메서드 (현재는 주석 처리됨)
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return imageFileName[row]
}
*/
// 각 행(row)에 표시할 "뷰(View)"를 반환하는 메서드
// 이 경우, 이미지뷰(UIImageView)를 만들어 반환함
func pickerView(
_ pickerView: UIPickerView,
viewForRow row: Int,
forComponent component: Int,
reusing view: UIView?
) -> UIView {
// 해당 행에 맞는 이미지를 이용해 UIImageView 생성
let imageView = UIImageView(image: imageArray[row])
// 이미지뷰의 크기를 지정 (픽커뷰 내부에 표시될 크기)
imageView.frame = CGRect(x: 0, y: 0, width: 100, height: 150)
// 완성된 UIImageView를 반환 → PickerView가 행에 표시함
return imageView
}
// 사용자가 PickerView의 특정 행(row)을 선택했을 때 호출되는 메서드
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// 선택된 파일 이름을 레이블에 표시
lblImageFileName.text = imageFileName[row]
// 선택된 이미지를 메인 이미지뷰에 표시
imageView.image = imageArray[row]
}
}
06 얼럿 사용해 경고 표시하기 : UIAlertController(알림창) 을 이용해서
램프를 켜기 / 끄기 / 제거하기 기능을 구현한 예제
//
// ViewController.swift
// Alert
//
// Created by BeomGeun Lee on ...
//
import UIKit // iOS UI를 구성하는 UIKit 프레임워크 불러오기
class ViewController: UIViewController {
// MARK: - 이미지 리소스 정의
// 전구 상태에 따라 표시할 세 가지 이미지 로드
let imgOn = UIImage(named: "lamp-on.png") // 전구 켜짐 이미지
let imgOff = UIImage(named: "lamp-off.png") // 전구 꺼짐 이미지
let imgRemove = UIImage(named: "lamp-remove.png")// 전구 제거된 이미지
// 현재 램프가 켜져 있는지 여부를 저장하는 변수
var isLampOn = true
// 스토리보드와 연결된 UIImageView (전구 이미지 표시용)
@IBOutlet var lampImg: UIImageView!
// MARK: - 생명주기 메서드
override func viewDidLoad() {
super.viewDidLoad()
// 화면이 처음 로드될 때 실행됨
// 앱 시작 시 기본 상태: 램프를 켜진 이미지로 설정
lampImg.image = imgOn
}
// MARK: - "램프 켜기" 버튼 액션
@IBAction func btnLampOn(_ sender: UIButton) {
// 만약 현재 램프가 이미 켜져 있다면
if isLampOn == true {
// UIAlertController 생성 (경고창 스타일)
let lampOnAlert = UIAlertController(
title: "경고",
message: "현재 On 상태입니다",
preferredStyle: .alert
)
// 확인 버튼 추가 (단순히 창만 닫음)
let onAction = UIAlertAction(
title: "네, 알겠습니다.",
style: .default,
handler: nil
)
// 버튼을 경고창에 추가
lampOnAlert.addAction(onAction)
// 경고창 표시
present(lampOnAlert, animated: true, completion: nil)
} else {
// 램프가 꺼져 있던 경우 → 켬
lampImg.image = imgOn
isLampOn = true
}
}
// MARK: - "램프 끄기" 버튼 액션
@IBAction func btnLanpOff(_ sender: UIButton) {
// 현재 램프가 켜져 있을 때만 동작
if isLampOn {
// 끌지 여부를 묻는 AlertController 생성
let lampOffAlert = UIAlertController(
title: "램프 끄기",
message: "램프를 끄시겠습니까?",
preferredStyle: .alert
)
// "네" 버튼 → 램프를 끄고 상태 변경
let offAction = UIAlertAction(
title: "네",
style: .default,
handler: { ACTION in
self.lampImg.image = self.imgOff
self.isLampOn = false
}
)
// "아니오" 버튼 → 아무 동작 안 함 (Alert만 닫힘)
let cancelAction = UIAlertAction(
title: "아니오",
style: .default,
handler: nil
)
// 두 버튼을 Alert에 추가
lampOffAlert.addAction(offAction)
lampOffAlert.addAction(cancelAction)
// Alert 표시
present(lampOffAlert, animated: true, completion: nil)
}
}
// MARK: - "램프 제거" 버튼 액션
@IBAction func btnLampRemove(_ sender: UIButton) {
// 램프 제거 여부를 묻는 알림창 생성
let lampRemoveAlert = UIAlertController(
title: "램프 제거",
message: "램프를 제거하시겠습니까?",
preferredStyle: .alert
)
// 첫 번째 버튼: "아니오, 끕니다(off)."
// → 램프를 꺼진 상태로 변경
let offAction = UIAlertAction(
title: "아니오, 끕니다(off).",
style: .default,
handler: { ACTION in
self.lampImg.image = self.imgOff
self.isLampOn = false
}
)
// 두 번째 버튼: "아니오, 켭니다(on)."
// → 램프를 켜진 상태로 변경
let onAction = UIAlertAction(
title: "아니오, 켭니다(on).",
style: .default,
handler: { ACTION in
self.lampImg.image = self.imgOn
self.isLampOn = true
}
)
// 세 번째 버튼: "네, 제거합니다."
// → 램프 이미지를 제거된 상태로 변경
// destructive 스타일: 빨간색 글씨로 표시됨 (주의를 주는 의미)
let removeAction = UIAlertAction(
title: "네, 제거합니다.",
style: .destructive,
handler: { ACTION in
self.lampImg.image = self.imgRemove
self.isLampOn = false
}
)
// 생성한 버튼 3개를 Alert에 추가
lampRemoveAlert.addAction(offAction)
lampRemoveAlert.addAction(onAction)
lampRemoveAlert.addAction(removeAction)
// Alert 표시
present(lampRemoveAlert, animated: true, completion: nil)
}
}
07 웹 뷰로 간단한 웹 브라우저 만들기

08 맵 뷰로 지도 나타내기: MapKit과 CoreLocation을 이용해서
📍현재 위치를 지도에 표시하고,
📍버튼(세그먼티드 컨트롤)을 눌러 다른 위치(예: 인덕대학교, 이지스퍼블리싱)를 보여주는 iOS 앱입니다.

//
// ViewController.swift
// Map
//
// Created by BeomGeun Lee on 2021
//
import UIKit
import MapKit // 지도 기능 제공 (MKMapView, MKAnnotation 등)
import CoreLocation // 위치 정보 관련 클래스(CLLocationManager 등) 사용
// CLLocationManagerDelegate: 위치 정보를 업데이트 받을 때 사용
class ViewController: UIViewController, CLLocationManagerDelegate {
// MARK: - IBOutlet 연결 (스토리보드 UI 요소)
@IBOutlet var myMap: MKMapView! // 지도 화면 표시용
@IBOutlet var lblLocationInfo1: UILabel! // 위치 설명 1줄 (예: "현재 위치" 등)
@IBOutlet var lblLocationInfo2: UILabel! // 위치 설명 2줄 (주소 등)
// 위치 관리를 담당할 매니저 객체 생성
let locationManager = CLLocationManager()
// MARK: - 뷰 로드 시 호출
override func viewDidLoad() {
super.viewDidLoad()
// 초기 라벨 내용 비우기
lblLocationInfo1.text = ""
lblLocationInfo2.text = ""
// 위치 매니저 설정
locationManager.delegate = self // 델리게이트 연결
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 최고 정확도로 위치 측정
locationManager.requestWhenInUseAuthorization() // 앱 실행 중 위치 접근 허용 요청 (권한 알림 표시)
locationManager.startUpdatingLocation() // 위치 정보 업데이트 시작
myMap.showsUserLocation = true // 지도에 파란 점(현재 위치) 표시
}
// MARK: - 지도 이동 함수
/// 특정 위도/경도 좌표와 확대 범위를 기준으로 지도 위치 이동
func goLocation(latitudeValue: CLLocationDegrees, longitudeValue : CLLocationDegrees, delta span :Double) -> CLLocationCoordinate2D {
// 위도, 경도를 CLLocationCoordinate2D 타입으로 생성
let pLocation = CLLocationCoordinate2DMake(latitudeValue, longitudeValue)
// 지도 확대/축소 범위(span): delta 값이 작을수록 더 확대됨
let spanValue = MKCoordinateSpan(latitudeDelta: span, longitudeDelta: span)
// 지도에 표시할 영역(region) 생성 (중심 좌표 + 확대 범위)
let pRegion = MKCoordinateRegion(center: pLocation, span: spanValue)
// 지도를 해당 지역으로 이동 (애니메이션 효과 포함)
myMap.setRegion(pRegion, animated: true)
// 좌표 리턴 (annotation 등에서 사용 가능)
return pLocation
}
// MARK: - 지도에 핀(마커) 표시 함수
func setAnnotation(latitudeValue: CLLocationDegrees,
longitudeValue: CLLocationDegrees,
delta span: Double,
title strTitle: String,
subtitle strSubtitle: String) {
// 핀(annotation) 객체 생성
let annotation = MKPointAnnotation()
// 지정된 위치로 지도 이동 후 해당 좌표를 annotation에 설정
annotation.coordinate = goLocation(latitudeValue: latitudeValue,
longitudeValue: longitudeValue,
delta: span)
// 핀 제목과 부제목 설정
annotation.title = strTitle
annotation.subtitle = strSubtitle
// 지도에 핀 추가
myMap.addAnnotation(annotation)
}
// MARK: - CLLocationManagerDelegate 메서드 (위치 업데이트 시 호출)
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// 배열의 마지막 위치(가장 최근 위치) 가져오기
let pLocation = locations.last
// 해당 위치로 지도 이동 (줌: delta 0.01 → 매우 가까이 확대)
_ = goLocation(latitudeValue: (pLocation?.coordinate.latitude)!,
longitudeValue: (pLocation?.coordinate.longitude)!,
delta: 0.01)
// 역지오코딩(좌표 → 주소 변환)
CLGeocoder().reverseGeocodeLocation(pLocation!, completionHandler: {
(placemarks, error) -> Void in
// placemarks 배열 중 첫 번째 정보 사용
let pm = placemarks!.first
// 국가 이름 가져오기
let country = pm!.country
var address: String = country!
// 도시(locality)가 존재할 경우 주소 문자열에 추가
if pm!.locality != nil {
address += " "
address += pm!.locality!
}
// 도로명(thoroughfare)이 존재할 경우 주소 문자열에 추가
if pm!.thoroughfare != nil {
address += " "
address += pm!.thoroughfare!
}
// 화면 라벨에 표시
self.lblLocationInfo1.text = "현재 위치"
self.lblLocationInfo2.text = address
})
// 한 번 위치를 가져온 뒤에는 추가 업데이트 중지 (배터리 절약)
locationManager.stopUpdatingLocation()
}
// MARK: - 세그먼티드 컨트롤 (지도 위치 변경)
@IBAction func sgChangeLocation(_ sender: UISegmentedControl) {
// 첫 번째 세그먼트 (현재 위치)
if sender.selectedSegmentIndex == 0 {
self.lblLocationInfo1.text = ""
self.lblLocationInfo2.text = ""
// 다시 위치 정보 업데이트 시작
locationManager.startUpdatingLocation()
}
// 두 번째 세그먼트 (인덕대학교)
else if sender.selectedSegmentIndex == 1 {
setAnnotation(latitudeValue: 37.6314191,
longitudeValue: 127.0548249,
delta: 1,
title: "인덕대학교",
subtitle: "서울특별시 노원구 초안산로 12")
self.lblLocationInfo1.text = "보고 계신 위치"
self.lblLocationInfo2.text = "인덕대학교"
}
// 세 번째 세그먼트 (이지스퍼블리싱)
else if sender.selectedSegmentIndex == 2 {
setAnnotation(latitudeValue: 37.556876,
longitudeValue: 126.914066,
delta: 0.1,
title: "이지스퍼블리싱",
subtitle: "서울시 마포구 잔다리로 109 이지스 빌딩")
self.lblLocationInfo1.text = "보고 계신 위치"
self.lblLocationInfo2.text = "이지스퍼블리싱 출판사"
}
}
}
09 페이지 이동하기 - 페이지 컨트롤: 이 코드는 UIPageControl을 이용해
📸 여러 이미지를 좌우로 넘기듯 보여주는 간단한 iOS 앱

페이지 컨트롤의 색상 변경
import UIKit
// 전역 변수로 이미지 파일 이름들을 저장한 배열
// -> 총 6장의 이미지를 순서대로 보여줄 예정
var images = [ "01.png", "02.png", "03.png", "04.png", "05.png", "06.png" ]
// MARK: - 메인 뷰 컨트롤러
class ViewController: UIViewController {
// MARK: - IBOutlet 연결 (스토리보드 UI 요소)
@IBOutlet var imgView: UIImageView! // 현재 선택된 이미지를 표시할 이미지 뷰
@IBOutlet var pageControl: UIPageControl! // 페이지(인디케이터) 컨트롤
// MARK: - 뷰가 로드될 때 실행되는 함수
override func viewDidLoad() {
super.viewDidLoad()
// 화면이 처음 로드될 때 실행되는 초기 설정 코드
// PageControl의 총 페이지 개수를 images 배열의 개수와 동일하게 설정
pageControl.numberOfPages = images.count
// 현재 선택된 페이지를 첫 번째(0번 인덱스)로 지정
pageControl.currentPage = 0
// 비활성(선택되지 않은) 페이지 인디케이터 색상 설정 (파란색)
pageControl.pageIndicatorTintColor = UIColor.blue
// 활성(현재 선택된) 페이지 인디케이터 색상 설정 (빨간색)
pageControl.currentPageIndicatorTintColor = UIColor.red
// 처음 앱을 실행할 때, 첫 번째 이미지를 이미지뷰에 표시
imgView.image = UIImage(named: images[0])
}
// MARK: - PageControl 값이 변경될 때 호출되는 액션 함수
@IBAction func pageChange(_ sender: UIPageControl) {
// 현재 PageControl의 선택된 인덱스(currentPage)에 맞는 이미지로 변경
imgView.image = UIImage(named: images[pageControl.currentPage])
}
}
10 탭 바 컨트롤러 이용해 여러 개의 뷰 넣기 : 가장 많이 사용, bmi 계산기 만들때도 사용

탭바 컨트롤러를 사용하여 여러 화면을 연결
11 내비게이션 컨트롤러 이용해 화면 전환하기 : 상세화면을 보여주거나 설정하는 등, 화면을 이동할때 Navigation controller 사용

12 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기 : 많이 사용하지만 어려워서 이번학기에 사용 X

13 음악 재생하고 녹음하기

14 비디오 재생 앱 만들기
//
// ViewController.swift
// MoviePlayer
//
// Created by Ho-Jeong Song on 2021/11/26.
//
import UIKit // 사용자 인터페이스 관련 클래스(View, Button 등)를 사용하기 위해 UIKit 프레임워크를 가져옵니다.
import AVKit // 오디오 및 비디오 콘텐츠 재생을 위한 표준 인터페이스(AVPlayerViewController 등)를 사용하기 위해 AVKit 프레임워크를 가져옵니다.
import AVFoundation // 비디오 재생 자체에 필요한 핵심 기능을 제공하지만, 여기서는 AVKit이 대부분의 작업을 처리합니다. (AVPlayer 사용을 위해 간접적으로 필요)
class ViewController: UIViewController {
// 뷰 컨트롤러가 메모리에 로드된 후 호출됩니다. 초기 설정을 수행하는 곳입니다.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// '내부 영화 재생' 버튼이 눌렸을 때 호출되는 액션 메서드입니다.
@IBAction func btnPlayInternalMovie(_ sender: UIButton) {
// 내부 파일 mp4 재생
// 1. 앱 번들(Bundle) 내에 있는 "FastTyping.mp4" 파일의 전체 경로를 찾습니다.
let filePath:String? = Bundle.main.path(forResource: "FastTyping", ofType: "mp4")
// 2. 찾은 파일 경로(String)를 사용하여 NSURL 객체를 생성합니다. (강제 언래핑 '!'을 사용하고 있습니다. 파일이 확실히 존재해야 합니다.)
let url = NSURL(fileURLWithPath: filePath!)
// 3. 비디오 재생을 시작하는 전용 함수를 호출합니다.
playVideo(url: url)
}
// '외부 영화 재생' 버튼이 눌렸을 때 호출되는 액션 메서드입니다.
@IBAction func btnPlayerExternalMovie(_ sender: UIButton) {
// 외부 파일 mp4 재생 (웹 URL을 이용)
// 1. 외부 서버에 있는 비디오 파일의 URL을 사용하여 NSURL 객체를 생성합니다. (강제 언래핑 '!'을 사용하고 있습니다. URL이 유효해야 합니다.)
let url = NSURL(string: "https://dl.dropboxusercontent.com/s/e38auz050w2mvud/Fireworks.mp4")!
// 2. 비디오 재생을 시작하는 전용 함수를 호출합니다.
playVideo(url: url)
}
// 주어진 URL을 사용하여 비디오를 재생하는 핵심 함수입니다.
private func playVideo(url: NSURL) {
// 1. 비디오 재생을 위한 뷰 컨트롤러인 AVPlayerViewController 인스턴스를 생성합니다.
let playerController = AVPlayerViewController()
// 2. 비디오 URL을 사용하여 비디오 재생을 관리하는 AVPlayer 인스턴스를 생성합니다.
let player = AVPlayer(url: url as URL)
// 3. AVPlayerViewController의 player 속성에 생성한 AVPlayer를 할당합니다.
playerController.player = player
// 4. 현재 뷰 컨트롤러(self)에서 AVPlayerViewController를 모달 방식으로 화면에 표시합니다.
// completion 클로저 { ... } 내의 코드는 화면 표시 애니메이션이 완료된 후 실행됩니다.
self.present(playerController, animated: true) {
// 5. 화면 표시 후, 비디오 재생을 시작합니다.
player.play()
}
}
}
15 카메라와 포토 라이브러리에서 미디어 가져오기 : 사진/비디오 촬영은 시뮬레이터로 불가, mac에 핸드폰 연결해서 해야함

16 코어 그래픽스로 화면에 그림 그리기
17 탭과 터치 사용해 스케치 앱 만들기 - 자주 사용함

//
// ViewController.swift
// Sketch
//
// Created by Ho-Jeong Song on 2021/12/01.
//
import UIKit // 사용자 인터페이스 관련 클래스(View, Touch, Image 등)를 사용하기 위해 UIKit 프레임워크를 가져옵니다.
class ViewController: UIViewController {
// 스토리보드에서 연결된 이미지 뷰(그림이 그려질 영역)입니다.
@IBOutlet var imgView: UIImageView!
// 터치 이벤트에서 이전 터치 지점의 좌표를 저장하는 변수입니다.
var lastPoint: CGPoint!
// 그릴 선의 두께(폭)를 정의합니다. (CGFloat 타입)
var lineSize:CGFloat = 2.0
// 그릴 선의 색상을 정의합니다. (Core Graphics에서 사용하는 CGColor 타입)
var lineColor = UIColor.red.cgColor
// 뷰 컨트롤러가 메모리에 로드된 후 호출됩니다.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// '지우기' 버튼이 눌렸을 때 호출되는 액션 메서드입니다.
@IBAction func btnClearImageView(_ sender: UIButton) {
// imgView의 image 속성을 nil로 설정하여 화면을 비웁니다.
imgView.image = nil
}
// 사용자가 화면을 터치하기 시작했을 때 호출됩니다.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 발생한 터치 이벤트 중 첫 번째 터치 객체를 가져옵니다.
let touch = touches.first! as UITouch
// 터치된 지점의 좌표를 imgView 내에서 계산하여 lastPoint에 저장합니다.
lastPoint = touch.location(in: imgView)
}
// 사용자가 화면을 터치한 채로 움직일 때(드래그) 계속해서 호출됩니다.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// 1. 새로운 그래픽 컨텍스트(Context)를 생성하고 imgView의 크기로 설정합니다.
UIGraphicsBeginImageContext(imgView.frame.size)
// 2. 현재 그래픽 컨텍스트에 그리기 설정을 합니다.
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor) // 선 색상 설정
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round) // 선 끝 모양을 둥글게 설정
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize) // 선 두께 설정
// 3. 현재 터치된 지점의 좌표를 가져옵니다.
let touch = touches.first! as UITouch
let currPoint = touch.location(in: imgView) // 현재 지점
// 4. 이전에 그려진 그림(imgView.image)을 새로 만든 컨텍스트에 그대로 그립니다. (덧그리기)
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 5. 이전 지점(lastPoint)에서 현재 지점(currPoint)까지 선을 그립니다.
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y)) // 선 그리기 시작점 설정
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: currPoint.x, y: currPoint.y)) // 현재 지점까지 선 추가
UIGraphicsGetCurrentContext()?.strokePath() // 실제로 선을 그립니다.
// 6. 현재 컨텍스트에 그려진 내용을 UIImage로 변환하여 imgView의 새로운 이미지로 설정합니다.
imgView.image = UIGraphicsGetImageFromCurrentImageContext()
// 7. 현재 그래픽 컨텍스트를 닫고 해제합니다.
UIGraphicsEndImageContext()
// 8. 현재 지점을 다음 움직임의 '이전 지점'으로 갱신합니다.
lastPoint = currPoint
}
// 사용자가 화면에서 손가락을 떼었을 때 호출됩니다.
// touchesEnded의 로직은 touchesMoved와 거의 동일하나,
// lastPoint에서 lastPoint로 '점'을 찍어 터치가 끝날 때 발생하는 미세한 딜레이/오차를 보정하고
// 터치 시작과 끝이 같은 지점일 때(점 찍기) 그림이 그려지도록 보장하는 역할도 합니다.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// 1. 새로운 그래픽 컨텍스트를 생성합니다.
UIGraphicsBeginImageContext(imgView.frame.size)
// 2. 그리기 설정을 합니다.
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor)
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round)
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize)
// 3. 이전에 그려진 그림을 컨텍스트에 덧그립니다.
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 4. 마지막 지점에 점을 찍어줍니다. (lastPoint -> lastPoint)
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.strokePath()
// 5. 새로운 이미지를 imgView에 할당하고 컨텍스트를 닫습니다.
imgView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
// 사용자가 기기를 흔들었을 때(쉐이크 제스처) 호출됩니다.
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
// 흔들림 동작(.motionShake)이 감지되면
if motion == .motionShake {
// 그림을 지웁니다.
imgView.image = nil
}
}
}

18 스와이프 제스처 사용하기 : UISwipeGestureRecognizer 사용
19 핀치 제스처 사용해 사진을 확대/축소하기
BMI 판정 앱

Apple App Store에서 찾아본 bmi 앱

1️⃣ 정의
- format: 형식 문자열(format string)
- C 언어나 Objective-C에서 쓰던 printf 스타일
- %d, %f, %@ 등 자리 표시자를 사용
- arguments: 가변 인자(variable arguments)
- format 문자열 안의 자리 표시자에 대응되는 값들을 순서대로 전달
2️⃣ 동작 원리
String(format:_:)은:
- format 문자열에서 %로 시작하는 플레이스홀더를 찾음
- arguments에서 순서대로 값을 가져와 그 위치에 치환함
- 결과를 새로운 String 객체로 만들어 반환
즉, 템플릿 + 값 → 최종 문자열 과정을 담당합니다.
-클래스로 만든 BMI 판정 코드
// bmiMode.swift
import Foundation
class BMI {
var weight : Double
var height : Double
init(weight:Double, height:Double){
self.height = height
self.weight = weight
}
func calcBMI() -> String {
let bmi=weight/(height*height*0.0001)// kg/m*m
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 = "저체중"
}
return "BMI:\(shortenedBmi), 판정:\(body)"
}
}
var yoo = BMI(weight:71.5, height:171.0)
print(yoo.calcBMI())
Storyboard vs SwiftUI
| 개념 | 시각적 UI 편집기에서 화면을 구성하고 ViewController와 연결 | 선언형(Declarative) 방식으로 코드로 UI를 구성 |
| 개발 방식 | 드래그 앤 드롭, Interface Builder 사용 | 코드 기반, 구조체와 뷰를 선언 |
| 학습 난이도 | 초보자 친화적, 시각적으로 바로 UI 확인 가능 | 처음에는 코드 중심이라 진입 장벽 있음 |
| 실시간 미리보기 | 제한적, 시뮬레이터/디바이스에서 확인 필요 | Canvas에서 실시간 미리보기 가능 |
| 재사용성 | ViewController 단위 재사용은 가능하지만 코드 재사용은 제한적 | View 단위 재사용 가능, 모듈화 쉬움 |
| 버전 관리(Git) | XML 파일 형태라 충돌 발생 시 해결 어려움 | 코드 기반이라 충돌 해결이 비교적 용이 |
| 호환성 | iOS 9 이상 | iOS 13 이상 |
| 레이아웃 | Auto Layout, Storyboard Constraints 사용 | SwiftUI의 레이아웃 시스템(HStack, VStack, ZStack 등) 사용 |
| 애니메이션 | 제한적, 직접 코드 작성 필요 | 선언형 애니메이션 지원, 간단히 구현 가능 |
| 프로토타입 제작 | 빠르게 화면 구성 가능 | 코드 작성 필요, 초기 구현 속도 느릴 수 있음 |
| 유지보수 | 화면이 많아지면 Storyboard가 커져 관리 어려움 | 작은 View 단위로 쪼개기 쉬워 유지보수 용이 |
Text Field의 keyboard Type : Decimal Pad - 숫자만 입력 가능

bmi 앱 만들기. 다음시간에 계속
'iOS' 카테고리의 다른 글
| iOS_week13 (1) | 2025.11.24 |
|---|---|
| iOS_week12 (0) | 2025.11.17 |
| iOS_week9 (0) | 2025.10.27 |
| iOS_week6 (0) | 2025.10.13 |
| iOS_week5 (0) | 2025.09.29 |