iOS 수업 내용 및 수업 후 과제

2025. 11. 11. 13:25iOS 기초/수업 내용 및 수업 후 과제

반응형

 Xcode 기본 개념

구분설명
Finder 사이드바 자주 사용하는 폴더를 추가해 프로젝트 접근성 향상
Issue Navigator 에러가 아닌 경고/이슈를 표시
Safe Area UI가 가려지지 않도록 안전 영역 확보 (게임 제외)
Storyboard 화살표 앱 시작 지점을 표시 — Is Initial View Controller 체크 필요
Attributes Inspector UI 속성(색상, 글꼴 등)을 설정
Connections Inspector IBOutlet / IBAction 연결관계 확인 (시험 출제 포인트 ✅)

Storyboard vs 🪄 SwiftUI

항목Storyboard (UIKit)SwiftUI
UI 구성 방식 드래그 & 드롭 (시각적) 선언형 코드 작성
미리보기 시뮬레이터 실행 필요 실시간 Canvas 미리보기
레이아웃 Auto Layout 수동 설정 자동 레이아웃 지원
상태 관리 ViewController 중심 @State, @Binding 등 데이터 바인딩
애니메이션 UIView.animate() .animation() 간단 설정
버전 iOS 5 이상 iOS 13 이상
학습 난이도 쉬움 초반 학습 필요
협업 디자이너 친화적 개발자 중심

 실습

 Hello World

가장 기본적인 UILabel, UIButton 연결 실습.


03. 이미지 뷰 — 전등 ON/OFF & 확대/축소

핵심 포인트:

  • UIImageView, UIButton, UISwitch 활용
  • 버튼으로 확대/축소
  • 스위치로 이미지 변경

ViewController.swift (리팩토링 버전)

import UIKit

final class ViewController: UIViewController {
    
    private var isZoomed = false
    private let scaleFactor: CGFloat = 2.0
    
    private let imageOn = UIImage(named: "lamp_on.png")
    private let imageOff = UIImage(named: "lamp_off.png")
    
    @IBOutlet private weak var imgView: UIImageView!
    @IBOutlet private weak var btnResize: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        imgView.image = imageOn
        btnResize.setTitle("축소", for: .normal)
    }
    
    @IBAction private func didTapResizeButton(_ sender: UIButton) {
        let scale = isZoomed ? 1 / scaleFactor : scaleFactor
        let newSize = CGSize(
            width: imgView.frame.width * scale,
            height: imgView.frame.height * scale
        )
        
        imgView.frame.size = newSize
        btnResize.setTitle(isZoomed ? "확대" : "축소", for: .normal)
        isZoomed.toggle()
    }
    
    @IBAction private func didToggleLampSwitch(_ sender: UISwitch) {
        imgView.image = sender.isOn ? imageOn : imageOff
    }
}
 
 

04. Date Picker — 날짜 및 시간 선택

핵심 포인트:

  • UIDatePicker, UILabel, Timer 활용
  • 1초마다 현재 시간 업데이트

ViewController.swift

 
import UIKit

class ViewController: UIViewController {
    
    let timeSelector: Selector = #selector(updateTime)
    let interval = 1.0
    var count = 0
    
    @IBOutlet var lblCurrentTime: UILabel!
    @IBOutlet var lblPickerTime: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        Timer.scheduledTimer(
            timeInterval: interval,
            target: self,
            selector: timeSelector,
            userInfo: nil,
            repeats: true
        )
    }
    
    @IBAction func changeDatePicker(_ sender: UIDatePicker) {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm EEE"
        
        lblPickerTime.text = "선택시간: " + formatter.string(from: sender.date)
    }
    
    @objc func updateTime() {
        let date = Date()
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
        
        lblCurrentTime.text = "현재시간: " + formatter.string(from: date)
    }
}

 06. Alert — 경고창 표시

핵심 포인트:

  • UIAlertController, UIAlertAction 사용
  • “켜기 / 끄기 / 제거” 상태 관리

ViewController.swift (리팩토링 버전)

 
import UIKit

class ViewController: UIViewController {
    
    let imgOn = UIImage(named: "lamp-on.png")
    let imgOff = UIImage(named: "lamp-off.png")
    let imgRemove = UIImage(named: "lamp-remove.png")
    
    var isLampOn = true
    
    @IBOutlet weak var lampImg: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        lampImg.image = imgOn
    }
    
    @IBAction func btnLampOn(_ sender: UIButton) {
        guard !isLampOn else {
            showAlert(title: "경고", message: "현재 On 상태입니다.")
            return
        }
        updateLamp(image: imgOn, isOn: true)
    }
    
    @IBAction func btnLampOff(_ sender: UIButton) {
        guard isLampOn else { return }
        
        let alert = UIAlertController(
            title: "램프 끄기",
            message: "램프를 끄시겠습니까?",
            preferredStyle: .alert
        )
        
        let offAction = UIAlertAction(title: "네", style: .default) { _ in
            self.updateLamp(image: self.imgOff, isOn: false)
        }
        
        alert.addAction(offAction)
        alert.addAction(UIAlertAction(title: "아니오", style: .cancel))
        
        present(alert, animated: true)
    }
    
    @IBAction func btnLampRemove(_ sender: UIButton) {
        let alert = UIAlertController(
            title: "램프 제거",
            message: "램프를 제거하시겠습니까?",
            preferredStyle: .alert
        )
        
        let off = UIAlertAction(title: "아니오, 끕니다(Off).", style: .default) { _ in
            self.updateLamp(image: self.imgOff, isOn: false)
        }
        
        let on = UIAlertAction(title: "아니오, 켭니다(On).", style: .default) { _ in
            self.updateLamp(image: self.imgOn, isOn: true)
        }
        
        let remove = UIAlertAction(title: "네, 제거합니다.", style: .destructive) { _ in
            self.updateLamp(image: self.imgRemove, isOn: false)
        }
        
        [off, on, remove].forEach { alert.addAction($0) }
        
        present(alert, animated: true)
    }
    
    private func updateLamp(image: UIImage?, isOn: Bool) {
        lampImg.image = image
        isLampOn = isOn
    }
    
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(
            title: title,
            message: message,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "확인", style: .default))
        present(alert, animated: true)
    }
}

 07. Web View — 간단한 웹 브라우저

핵심 포인트:

  • WKWebView, UITextField, UIActivityIndicatorView
  • URL 입력 및 사이트 이동, HTML 로드

 08. Map View — 지도 표시

핵심 포인트:

  • MKMapView, CLLocationManager, CLGeocoder
  • 현재 위치 표시 및 특정 지역 Annotation 추가

 14. Movie Player — 동영상 재생

핵심 포인트:

  • AVPlayerViewController 활용
  • 내부/외부 mp4 파일 재생

📄 ViewController.swift (리팩토링 버전)

import UIKit
import AVKit

final class ViewController: UIViewController {
    
    @IBAction private func playInternalVideo(_ sender: UIButton) {
        guard let path = Bundle.main.path(forResource: "FastTyping", ofType: "mp4") else {
            showErrorAlert(message: "내부 비디오를 찾을 수 없습니다.")
            return
        }
        playVideo(with: URL(fileURLWithPath: path))
    }
    
    @IBAction private func playExternalVideo(_ sender: UIButton) {
        guard let url = URL(string: "https://dl.dropboxusercontent.com/s/e38auz050w2mvud/Fireworks.mp4") else {
            showErrorAlert(message: "잘못된 URL입니다.")
            return
        }
        playVideo(with: url)
    }
    
    private func playVideo(with url: URL) {
        let player = AVPlayer(url: url)
        let controller = AVPlayerViewController()
        controller.player = player
        
        present(controller, animated: true) {
            player.play()
        }
    }
    
    private func showErrorAlert(message: String) {
        let alert = UIAlertController(
            title: "에러",
            message: message,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "확인", style: .default))
        present(alert, animated: true)
    }
}

🖌️ 16~17. Draw Graphics & Sketch

핵심 포인트:

  • UIGraphicsBeginImageContext 로 도형 그리기
  • touchesMoved() 로 손가락 드로잉 구현
  • motionEnded() 로 흔들면 전체 지우기 기능

- 자주 사용하는 폴더는 사이드바에서 사용

- label은 Outlet, Button은 Action

+ 와 - 버튼으로 +1 , -1 증감시키는 소스

//
//  ViewController.swift
//  UpDown
//
//  Created by Induk cs on 2025/11/04.
//

import UIKit

class ViewController: UIViewController {
    var x = 0

    @IBOutlet weak var lblNumber: UILabel!

    @IBAction func btnUp(_ sender: UIButton) {
        x = x + 1
        lblNumber.text = String(x)
    }

    @IBAction func btnDown(_ sender: UIButton) {
        x = x - 1
        lblNumber.text = String(x)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
}

 

connects inspector는 시험에 나옴 (연결관계 확인하는 inspetor는 무엇입니까?) -> connects inspector

시험에 잘나옴

- 버튼은 Outlet으로 만들 수도 있고, Action으로 만들 수도 있음

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

이미지 확대/축소, ON/OFF 프로그램

import UIKit
// UIKit 프레임워크를 불러옴 (iOS의 UI 요소를 사용하기 위해 필요)

class ViewController: UIViewController {
    // UIViewController를 상속받아 화면(뷰 컨트롤러)을 정의함
    
    var isZoom = false
    // 이미지가 확대되어 있는지 여부를 저장하는 변수 (false면 기본 크기)
    
    var imgOn: UIImage?
    // "램프 켜짐" 이미지를 저장할 변수
    
    var imgOff: UIImage?
    // "램프 꺼짐" 이미지를 저장할 변수

    @IBOutlet var imgView: UIImageView!
    // 스토리보드에서 이미지뷰(UIImageView)를 연결하기 위한 아웃렛 변수
    
    @IBOutlet var btnResize: UIButton!
    // 스토리보드에서 버튼(UIButton)을 연결하기 위한 아웃렛 변수
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 화면이 처음 로드될 때 한 번만 호출되는 메서드
        
        imgOn = UIImage(named: "lamp_on.png")
        // 앱 리소스에 있는 "lamp_on.png" 파일을 불러와 imgOn 변수에 저장
        
        imgOff = UIImage(named: "lamp_off.png")
        // 앱 리소스에 있는 "lamp_off.png" 파일을 불러와 imgOff 변수에 저장
        
        imgView.image = imgOn
        // 초기 이미지뷰에 켜진 램프 이미지를 설정
    }

    @IBAction func btnResizeImage(_ sender: UIButton) {
        // "확대/축소" 버튼이 눌렸을 때 호출되는 액션 메서드
        
        let scale: CGFloat = 2.0
        // 이미지를 확대/축소할 비율을 상수로 정의 (2배)
        
        var newWidth: CGFloat, newHeight: CGFloat
        // 계산된 새 이미지 크기를 저장할 변수를 선언
        
        if (isZoom) {   // true → 현재 확대된 상태라면 축소 실행
            newWidth = imgView.frame.width / scale
            // 현재 이미지의 너비를 절반으로 줄임
            
            newHeight = imgView.frame.height / scale
            // 현재 이미지의 높이를 절반으로 줄임
            
            btnResize.setTitle("확대", for: .normal)
            // 버튼의 텍스트를 "확대"로 변경
        }
        else {  // false → 현재 축소된 상태라면 확대 실행
            newWidth = imgView.frame.width * scale
            // 현재 이미지의 너비를 2배로 늘림
            
            newHeight = imgView.frame.height * scale
            // 현재 이미지의 높이를 2배로 늘림
            
            btnResize.setTitle("축소", for: .normal)
            // 버튼의 텍스트를 "축소"로 변경
        }
        
        imgView.frame.size = CGSize(width: newWidth, height: newHeight)
        // 이미지뷰의 프레임 크기를 계산된 크기로 변경
        
        isZoom = !isZoom
        // 현재 확대 상태를 반전시킴 (true ↔ false)
    }
    
    @IBAction func switchImageOnOff(_ sender: UISwitch) {
        // 스위치가 켜지거나 꺼질 때 호출되는 액션 메서드
        
        if sender.isOn {
            // 스위치가 "ON" 상태이면
            imgView.image = imgOn
            // 켜진 램프 이미지를 표시
        } else {
            // 스위치가 "OFF" 상태이면
            imgView.image = imgOff
            // 꺼진 램프 이미지를 표시
        }
    }
}
- 출처:  Do it! 스위프트로 아이폰 앱 만들기 입문 [ 개정8판 ] 03 원하는 이미지 화면에 출력하기 - 이미지 뷰

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

 

Date Picker

//
//  ViewController.swift
//  DatePicker
//
//  Created by Ho-Jeong Song on 2021/11/24.
//

import UIKit
// UIKit 프레임워크를 가져옴 (iOS의 UI 구성요소 사용을 위해 필요)

class ViewController: UIViewController {
    // UIViewController를 상속받는 ViewController 클래스 정의 (화면 하나를 담당)
    
    let timeSelector: Selector = #selector(ViewController.updateTime)
    // 타이머가 호출할 함수(메서드)를 지정하는 Selector 생성
    // 여기서는 updateTime() 함수가 매초 호출됨
    
    let interval = 1.0
    // 타이머의 반복 간격(초 단위). 1초마다 동작하도록 설정
    
    var count = 0
    // (현재 사용하지 않지만) 시간 경과 횟수를 저장할 변수. 테스트용으로 주로 사용됨

    @IBOutlet var lblCurrentTime: UILabel!
    // 스토리보드의 "현재시간"을 표시하는 UILabel과 연결되는 아웃렛 변수
    
    @IBOutlet var lblPickerTime: UILabel!
    // 스토리보드의 "선택시간"을 표시하는 UILabel과 연결되는 아웃렛 변수
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 뷰가 메모리에 로드된 직후 한 번만 호출되는 생명주기 메서드
        
        // 타이머 생성: interval(1초)마다 timeSelector(updateTime 함수)를 호출
        Timer.scheduledTimer(
            timeInterval: interval,   // 반복 간격(1초)
            target: self,             // 타이머가 호출할 대상 (현재 ViewController)
            selector: timeSelector,   // 호출할 함수 (updateTime)
            userInfo: nil,            // 추가 정보 없음
            repeats: true             // 반복 실행 설정 (true)
        )
    }

    @IBAction func changeDatePicker(_ sender: UIDatePicker) {
        // 사용자가 UIDatePicker의 날짜/시간을 변경했을 때 호출되는 함수
        
        let datePickerView = sender
        // 전달된 UIDatePicker 객체를 지역 변수에 저장 (가독성 향상 목적)
        
        let formatter = DateFormatter()
        // 날짜와 시간을 특정 형식의 문자열로 바꾸기 위한 포맷터 객체 생성
        
        formatter.dateFormat = "yyyy-MM-dd HH:mm EEE"
        // 날짜 포맷 설정
        // 예: 2025-11-04 14:30 Tue
        
        lblPickerTime.text = "선택시간: " + formatter.string(from: datePickerView.date)
        // 사용자가 고른 날짜를 지정한 포맷으로 변환해 "선택시간:" 레이블에 표시
    }

    @objc func updateTime() {
        // 타이머에 의해 1초마다 호출되는 메서드
        // @objc 키워드는 Selector로 참조될 수 있도록 Objective-C 런타임에서 접근 가능하게 함
        
        // lblCurrentTime.text = String(count)
        // count = count + 1
        // (테스트용 코드: 타이머 호출 횟수를 표시하려고 사용했던 흔적, 현재는 주석 처리)
        
        let date = NSDate()
        // 현재 시간을 NSDate 객체로 가져옴 (Swift의 Date와 거의 동일)
        
        let formatter = DateFormatter()
        // 날짜를 문자열로 변환하기 위한 포맷터 객체 생성
        
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss EEE"
        // 표시할 날짜 및 시간 포맷 설정
        // 예: 2025-11-04 14:30:15 Tue
        
        lblCurrentTime.text = "현재시간: " + formatter.string(from: date as Date)
        // 현재 시간을 지정된 형식으로 변환하여 "현재시간:" 레이블에 표시
    }
}
- 출처:  Do it! 스위프트로 아이폰 앱 만들기 입문 [ 개정8판 ] 04 데이트 피커 사용해 날짜 선택하기

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

PickerView

- UIViewControl는 부모, 나머지는 프로토콜(친구)

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

Alert

//
//  ViewController.swift
//  Alert
//
//  Created by BeomGeun Lee on
//

import UIKit

class ViewController: UIViewController {
    
    // MARK: - Properties
    let imgOn = UIImage(named: "lamp-on.png")
    let imgOff = UIImage(named: "lamp-off.png")
    let imgRemove = UIImage(named: "lamp-remove.png")
    
    var isLampOn = true
    
    @IBOutlet weak var lampImg: UIImageView!
    
    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        lampImg.image = imgOn
    }

    // MARK: - Actions
    
    /// "켜기" 버튼 클릭 시 호출
    @IBAction func btnLampOn(_ sender: UIButton) {
        guard !isLampOn else {
            showAlert(title: "경고", message: "현재 On 상태입니다.")
            return
        }
        updateLamp(image: imgOn, isOn: true)
    }
    
    /// "끄기" 버튼 클릭 시 호출
    @IBAction func btnLampOff(_ sender: UIButton) {
        guard isLampOn else { return }
        
        let alert = UIAlertController(
            title: "램프 끄기",
            message: "램프를 끄시겠습니까?",
            preferredStyle: .alert
        )
        
        let offAction = UIAlertAction(title: "네", style: .default) { _ in
            self.updateLamp(image: self.imgOff, isOn: false)
        }
        
        let cancelAction = UIAlertAction(title: "아니오", style: .cancel)
        
        alert.addAction(offAction)
        alert.addAction(cancelAction)
        present(alert, animated: true)
    }
    
    /// "제거" 버튼 클릭 시 호출
    @IBAction func btnLampRemove(_ sender: UIButton) {
        let alert = UIAlertController(
            title: "램프 제거",
            message: "램프를 제거하시겠습니까?",
            preferredStyle: .alert
        )
        
        let offAction = UIAlertAction(title: "아니오, 끕니다(Off).", style: .default) { _ in
            self.updateLamp(image: self.imgOff, isOn: false)
        }
        
        let onAction = UIAlertAction(title: "아니오, 켭니다(On).", style: .default) { _ in
            self.updateLamp(image: self.imgOn, isOn: true)
        }
        
        let removeAction = UIAlertAction(title: "네, 제거합니다.", style: .destructive) { _ in
            self.updateLamp(image: self.imgRemove, isOn: false)
        }
        
        [offAction, onAction, removeAction].forEach { alert.addAction($0) }
        present(alert, animated: true)
    }
    
    // MARK: - Helper Methods
    
    /// 램프 이미지를 변경하고 상태를 업데이트
    private func updateLamp(image: UIImage?, isOn: Bool) {
        lampImg.image = image
        isLampOn = isOn
    }
    
    /// 단순 알림창 표시 (확인 버튼 1개)
    private func showAlert(title: String, message: String) {
        let alert = UIAlertController(
            title: title,
            message: message,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "확인", style: .default))
        present(alert, animated: true)
    }
}
소스 정리(리펙토링) 함