(Identity inspecter)class 연결 해줘야됨 Video ViewController, 반드시enter(enter
Identity inspector: video Scene 스토리 보드와 VideoViewController클래스 연결
시험
present 함수는 iOS의 UIKit 프레임워크에서 제공하는 메서드로, 현재의 뷰 컨트롤러에서 다른 뷰 컨트롤러를 모달 방식으로 표시하는 데 사용됩니다. 이 메서드는 사용자 인터페이스의 흐름을 관리하는 데 중요한 역할을 하며, 새로운 화면을 사용자에게 보여줄 때 주로 사용됩니다.
//
// VideoViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/27.
//
import UIKit // UIKit 프레임워크를 임포트하여 UI 요소를 사용할 수 있게 합니다.
import AVKit // AVKit 프레임워크를 임포트하여 비디오 재생 기능을 사용할 수 있게 합니다.
class VideoViewController: UIViewController { // VideoViewController 클래스를 정의하며 UIViewController를 상속받습니다.
@IBAction func playVideo(_ sender: UIButton) { // 버튼 클릭 시 호출되는 액션 메서드입니다.
// 비디오 파일의 경로를 가져옵니다. APT.mp4 파일이 번들에 존재하는지 확인합니다.
let videoPath: String? = Bundle.main.path(forResource: "APT", ofType: "mp4")
// 비디오 파일 경로가 nil이 아닐 경우에만 진행합니다.
guard let path = videoPath else {
print("비디오 파일을 찾을 수 없습니다.") // 비디오 파일이 없을 경우 에러 메시지를 출력합니다.
return // 비디오 파일이 없으면 함수 실행을 종료합니다.
}
// 비디오 파일의 URL을 생성합니다. URL(filePath:)는 파일 경로를 URL로 변환합니다.
let videoURL = URL(fileURLWithPath: path)
// AVPlayer 인스턴스를 생성하여 비디오 URL을 전달합니다.
let player = AVPlayer(url: videoURL)
// AVPlayerViewController 인스턴스를 생성하여 비디오 플레이어 UI를 표시합니다.
let playerController = AVPlayerViewController()
playerController.player = player // 생성한 AVPlayer를 playerController에 할당합니다.
// 비디오 플레이어 화면을 모달로 표시합니다.
present(playerController, animated: true) {
player.play() // 비디오 재생을 시작합니다.
}
}
override func viewDidLoad() { // 뷰가 메모리에 로드된 후 호출되는 메서드입니다.
super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출합니다.
// 추가적인 설정을 여기에 작성할 수 있습니다.
}
}
//
// VideoViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/27.
//
import UIKit
import AVKit
class VideoViewController: UIViewController {
@IBAction func playVideo(_ sender: UIButton) {
guard let videoPath = Bundle.main.path(forResource: "APT", ofType: "mp4")else{return} //옵셔널 형을 풀기 위해서 guard let사용
let videoURL = URL(filePath: videoPath)
let player = AVPlayer(url: videoURL)
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true)//시험
player.play()
// // 비디오 파일의 URL을 가져옵니다.
// guard let videoURL = Bundle.main.url(forResource: "APT", withExtension: "mp4") else {
// print("비디오 파일을 찾을 수 없습니다.")
// return
// }
//
// // AVPlayer와 AVPlayerViewController를 생성합니다.
// let player = AVPlayer(url: videoURL)
// let playerViewController = AVPlayerViewController()
// playerViewController.player = player
//
// // 비디오 플레이어 화면을 표시합니다.
// present(playerViewController, animated: true) {
// player.play() // 비디오 재생 시작
// }
// }
//
// override func viewDidLoad() {
// super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
옵셔널 형을 풀기 위해서 guard let사용
enter키 누르면 ↓
디폴트 인자(default arguments)는 함수나 메서드를 호출할 때 인자를 생략할 수 있도록 해주는 기능입니다. 각 프로그래밍 언어에서 디폴트 인자를 사용하는 방법을 예)
//
// AppDelegate.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
returntrue
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
refactoring하기
//
// WebViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/27.
//
import UIKit
import WebKit
/// `WebViewController`는 웹 페이지를 표시하는 뷰 컨트롤러입니다.
class WebViewController: UIViewController {
/// WKWebView 인스턴스. 웹 콘텐츠를 표시하는 뷰입니다.
@IBOutlet weak var webView: WKWebView!
/// 뷰가 로드될 때 호출되는 메서드.
/// 초기 웹 페이지를 로드합니다.
override func viewDidLoad() {
super.viewDidLoad()
loadURL("https://hhh08.tistory.com/") // 초기 로드할 URL
}
/// Naver 버튼이 클릭될 때 호출되는 액션 메서드.
/// Naver 모바일 사이트를 로드합니다.
/// - Parameter sender: 버튼 클릭 이벤트의 발신자
@IBAction func goNaver(_ sender: UIButton) {
loadURL("https://m.naver.com") // Naver 모바일 사이트 로드
}
/// 주어진 URL 문자열을 사용하여 웹 페이지를 로드하는 메서드.
/// - Parameter urlString: 로드할 웹 페이지의 URL 문자열
private func loadURL(_ urlString: String) {
// URL 문자열을 URL 객체로 변환
guard let url = URL(string: urlString) else { return }
// URLRequest 객체 생성
let request = URLRequest(url: url)
// 웹 뷰에 요청 로드
webView.load(request)
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
webviewcontroller문서화하기
//
// VideoViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/27.
//
import UIKit
import AVKit
/// `VideoViewController`는 비디오를 재생하는 뷰 컨트롤러입니다.
class VideoViewController: UIViewController {
/// 비디오 재생 버튼이 클릭될 때 호출되는 액션 메서드.
/// - Parameter sender: 버튼 클릭 이벤트의 발신자
@IBAction func playVideo(_ sender: UIButton) {
// 비디오 파일의 경로를 가져옵니다. 파일이 존재하지 않으면 메서드를 종료합니다.
guard let videoPath = Bundle.main.path(forResource: "APT", ofType: "mp4") else { return }
// 비디오 파일의 URL을 생성합니다.
let videoURL = URL(fileURLWithPath: videoPath)
// AVPlayer 인스턴스를 생성하여 비디오 URL을 설정합니다.
let player = AVPlayer(url: videoURL)
// AVPlayerViewController 인스턴스를 생성합니다.
let playerController = AVPlayerViewController()
playerController.player = player
// 비디오 플레이어 화면을 모달로 표시하고, 표시가 완료된 후 비디오 재생을 시작합니다.
self.present(playerController, animated: true) {
player.play() // 비디오 재생 시작
}
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
videoviewcontroller문서화하기
//
// ViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
/// `ViewController`는 BMI(체질량지수)를 계산하고 결과를 표시하는 뷰 컨트롤러입니다.
class ViewController: UIViewController {
// 사용자로부터 키와 체중을 입력받기 위한 UI 요소
@IBOutlet weak var txtHeight: UITextField! // 키 입력 필드
@IBOutlet weak var txtWeight: UITextField! // 체중 입력 필드
@IBOutlet weak var lblResult: UILabel! // BMI 결과를 표시할 레이블
/// BMI 계산 버튼이 클릭될 때 호출되는 액션 메서드.
/// - Parameter sender: 버튼 클릭 이벤트의 발신자
@IBAction func calcBmi(_ sender: UIButton) {
// 키와 체중 입력 필드가 비어 있는지 확인합니다.
if txtHeight.text == "" || txtWeight.text == "" {
lblResult.textColor = .red // 텍스트 색상을 빨간색으로 설정
lblResult.text = "키와 체중을 입력하세요!" // 안내 메시지 표시
return
} else {
// 입력된 체중과 키를 Double로 변환합니다.
let weight = Double(txtWeight.text!)!
let height = Double(txtHeight.text!)!
// BMI 계산 (kg/m²)
let bmi = weight / (height * height * 0.0001) // height는 cm 단위이므로 0.0001로 나눔
let shortenedBmi = String(format: "%.1f", bmi) // 소수점 첫째 자리까지 포맷팅
var body = "" // BMI 판정 결과를 저장할 변수
var color = UIColor.white // 초기값으로 흰색 설정
// BMI 값에 따라 판정 및 색상 설정
if bmi >= 40 {
color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) // 3단계 비만: 빨간색
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
color = UIColor(displayP3Red: 0.7, green: 0.0, blue: 0.0, alpha: 1.0) // 2단계 비만: 어두운 빨간색
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
color = UIColor(displayP3Red: 0.4, green: 0.0, blue: 0.0, alpha: 1.0) // 1단계 비만: 중간 빨간색
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
color = UIColor(displayP3Red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0) // 정상: 파란색
body = "정상"
} else {
color = UIColor(displayP3Red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) // 저체중: 초록색
body = "저체중"
}
// 결과 레이블의 배경색과 텍스트 설정
lblResult.backgroundColor = color
lblResult.clipsToBounds = true // 레이블의 경계가 둥글게 설정
lblResult.layer.cornerRadius = 10 // 레이블의 모서리를 둥글게 설정
lblResult.text = "BMI:\(shortenedBmi), 판정: \(body)" // 최종 결과 표시
}
}
/// 뷰가 로드될 때 호출되는 메서드.
override func viewDidLoad() {
super.viewDidLoad()
// 추가적인 설정을 여기에 작성할 수 있습니다.
}
}
issue navigator이 떴을때 iOS에서 UI 요소의 위치와 크기를 동적으로 조정하기 위한 시스템 인 오토 레이아웃(Autolayout)이 필요
//
// ViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var txtHeight: UITextField!
@IBOutlet weak var txtWeight: UITextField!
@IBOutlet weak var lblResult: UILabel!
@IBAction func calcBmi(_ sender: UIButton) {
let weight = 60.0
let height = 170.0
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""if bmi >= 40 {
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
print("BMI:\(shortenedBmi), 판정:\(body)")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
버튼을 누를때 앱에서는 안보이고 debug area에 보임
//
// ViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var txtHeight: UITextField!
@IBOutlet weak var txtWeight: UITextField!
@IBOutlet weak var lblResult: UILabel!
@IBAction func calcBmi(_ sender: UIButton) {
let weight = Double(txtWeight.text!)!//txtWeight.text은 옵셔널 string임 강제 언래핑!해야됨, double도 강제 언래핑! /text필드에 둘 다 값을 넣지 않으면 crash가 남
let height = Double(txtHeight.text!)!
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""if bmi >= 40 {
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
print("BMI:\(shortenedBmi), 판정:\(body)")
lblResult.text = "BMI:\(shortenedBmi), 판정: \(body)"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
let height = Double(txtHeight.text!)!
let weight = Double(txtWeight.text!)!
첫 번째 !: txtHeight.text와 txtWeight.text는 옵셔널(Optional) 타입입니다. 즉, 이 값이 nil일 수도 있음을 나타냅니다. !를 사용하여 이 옵셔널 값을 강제로 언래핑하여 실제 문자열 값을 가져옵니다. 만약 이 값이 nil이라면 런타임 오류가 발생합니다.
두 번째 !: Double(txtHeight.text!)와 Double(txtWeight.text!)는 문자열을 Double 타입으로 변환하는 시도입니다. 문자열이 숫자로 변환될 수 없는 경우 nil이 반환됩니다. 따라서 이 값을 다시 강제로 언래핑하여 Double 값을 가져옵니다. 이 경우에도 nil이라면 런타임 오류가 발생합니다.
<guard let을 사용하여 코드 수정>
guard let heightText = txtHeight.text,
let height = Double(heightText) else {
print("Height 입력값이 올바르지 않습니다.")
return
}
print("Height: \(height)")
guard let weightText = txtWeight.text,
let weight = Double(weightText) else {
print("Weight 입력값이 올바르지 않습니다.")
return
}
print("Weight: \(weight)")
//
// ViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var txtHeight: UITextField!
@IBOutlet weak var txtWeight: UITextField!
@IBOutlet weak var lblResult: UILabel!
@IBAction func calcBmi(_ sender: UIButton) {
if txtHeight.text == "" || txtWeight.text == "" {//값이 없으면 안내문이 뜨고 , 색을 red 로 지정함
lblResult.textColor = .red
lblResult.text = "키와 체중을 입력하세요!"return
}else{
let weight = Double(txtWeight.text!)!
let height = Double(txtHeight.text!)!
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""if bmi >= 40 {
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
print("BMI:\(shortenedBmi), 판정:\(body)")
lblResult.text = "BMI:\(shortenedBmi), 판정: \(body)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
전체 선택후 ctrl +i : 소스 정렬
//
// ViewController.swift
// BMI_kdh
//
// Created by 소프트웨어컴퓨터 on 2024/11/13.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var txtHeight: UITextField! // 키 입력 필드
@IBOutlet weak var txtWeight: UITextField! // 체중 입력 필드
@IBOutlet weak var lblResult: UILabel! // 결과를 출력할 레이블
@IBAction func calcBmi(_ sender: UIButton) { // BMI 계산 버튼 클릭 시 호출되는 함수
// 입력값이 비어 있는지 확인
if txtHeight.text == "" || txtWeight.text == "" {
lblResult.textColor = .red // 경고 메시지 색상 설정
lblResult.text = "키와 체중을 입력하세요!" // 안내 메시지 출력
return // 함수 종료
} else {
// 입력된 키와 체중을 Double로 변환
let weight = Double(txtWeight.text!)!
let height = Double(txtHeight.text!)!
// BMI 계산 (kg/m²)
let bmi = weight / (height * height * 0.0001)
let shortenedBmi = String(format: "%.1f", bmi) // 소수점 1자리로 포맷팅
var body = "" // BMI 판정을 저장할 변수
var color = UIColor.white // 초기 배경색을 하얀색으로 설정
// BMI 값에 따른 판정 및 색상 설정
if bmi >= 40 {
color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) // 3단계 비만 (빨간색)
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
color = UIColor(displayP3Red: 0.7, green: 0.0, blue: 0.0, alpha: 1.0) // 2단계 비만 (어두운 빨간색)
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
color = UIColor(displayP3Red: 0.4, green: 0.0, blue: 0.0, alpha: 1.0) // 1단계 비만 (좀 더 어두운 빨간색)
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
color = UIColor(displayP3Red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0) // 정상 (파란색)
body = "정상"
} else {
color = UIColor(displayP3Red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) // 저체중 (초록색)
body = "저체중"
}
// 결과 레이블의 배경색 설정
lblResult.backgroundColor = color
lblResult.clipsToBounds = true // 레이블의 경계선 처리
lblResult.layer.cornerRadius = 10 // 레이블의 모서리를 둥글게 설정
lblResult.text = "BMI:\(shortenedBmi), 판정: \(body)" // 결과 출력
}
}
override func viewDidLoad() {
super.viewDidLoad()
// 뷰가 로드된 후 추가 설정이 필요할 경우 여기에 작성
}
}
결과 나올때 결과에 따라 배경색이 바뀌는 코드 + 결과 레이블 둥글게하는 코드 추가
universal: 고해상도 ( 아이패드, 아이폰 사진 전부 가능하게하는 )
tab bar controller 선택하면 화면이 하나 더 생김
(apple hig: apple 디자인 가이드라인)
tab bar controller 화면을 ctrl누르고 드래그 후 view controllers 누르기
import UIKit
// 이미지 파일 이름을 저장하는 배열
var images = [ "01.png", "02.png", "03.png", "04.png", "05.png", "06.png" ]
class ViewController: UIViewController {
// 이미지 뷰와 페이지 컨트롤을 아울렛으로 연결
@IBOutlet var imgView: UIImageView!
@IBOutlet var pageControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
// 뷰가 로드된 후 추가 설정
// 페이지 컨트롤의 페이지 수를 이미지 배열의 수로 설정
pageControl.numberOfPages = images.count
// 현재 페이지를 3으로 설정 (4번째 이미지)
pageControl.currentPage = 3
// 페이지 인디케이터 색상 설정
pageControl.pageIndicatorTintColor = UIColor.blue
pageControl.currentPageIndicatorTintColor = UIColor.yellow
// 초기 이미지 설정 (첫 번째 이미지)
imgView.image = UIImage(named: images[0])
}
// 페이지 컨트롤의 값이 변경될 때 호출되는 메서드
@IBAction func pageChange(_ sender: UIPageControl) {
// 현재 페이지에 해당하는 이미지를 이미지 뷰에 설정
imgView.image = UIImage(named: images[pageControl.currentPage])
}
}
10 탭 바 컨트롤러 이용해 여러 개의 뷰 넣기
-tabbar controller
화면 밑에 tab으로 다른 화면으로 빠르게 이동해줌
-navigation controller
앱에서 화면 간의 전환을 쉽게 만들기 위해 사용
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 재사용 가능한 셀을 큐에서 가져옵니다. "myCell" 식별자를 사용합니다.
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
// items 배열에서 현재 행의 데이터로 텍스트 레이블을 설정합니다.
cell.textLabel?.text = items[(indexPath as NSIndexPath).row]
// itemsImageFile 배열에서 현재 행의 이미지로 이미지 뷰를 설정합니다.
cell.imageView?.image = UIImage(named: itemsImageFile[(indexPath as NSIndexPath).row])
// 구성된 셀을 반환합니다.
return cell
}
12 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기
//
// TableViewController.swift
// Table
//
// Created by BeomGeun Lee on 2021.
//
import UIKit
// 아이템 목록과 이미지 파일 이름 배열
var items = ["책 구매", "철수와 약속", "스터디 준비하기"]
var itemsImageFile = ["cart.png", "clock.png", "pencil.png"]
class TableViewController: UITableViewController {
@IBOutlet var tvListView: UITableView! // 테이블 뷰 아울렛
override func viewDidLoad() {
super.viewDidLoad()
// 선택 상태를 유지하려면 주석 해제
// self.clearsSelectionOnViewWillAppear = false
// 내비게이션 바에 편집 버튼 추가
self.navigationItem.leftBarButtonItem = self.editButtonItem
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 뷰가 나타날 때 테이블 뷰를 갱신
tvListView.reloadData()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// 테이블 뷰의 섹션 수 반환
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// 섹션에 있는 행의 수 반환
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// 재사용 가능한 셀을 큐에서 가져옴
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
// 해당 행의 텍스트와 이미지를 설정
cell.textLabel?.text = items[(indexPath as NSIndexPath).row]
cell.imageView?.image = UIImage(named: itemsImageFile[(indexPath as NSIndexPath).row])
return cell
}
// Override to support conditional editing of the table view.
// 셀 편집 가능 여부 설정
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
returntrue
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// 삭제 버튼 클릭 시 데이터 소스에서 해당 아이템 삭제
items.remove(at: (indexPath as NSIndexPath).row)
itemsImageFile.remove(at: (indexPath as NSIndexPath).row)
tableView.deleteRows(at: [indexPath], with: .fade) // 테이블 뷰에서 행 삭제
} elseif editingStyle == .insert {
// 새로운 아이템 추가 시 처리 (현재는 구현 안 됨)
}
}
override func tableView(_ tableView: UITableView, titleForDeleteConfirmationButtonForRowAt indexPath: IndexPath) -> String? {
// 삭제 확인 버튼의 제목 설정
return"삭제"
}
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
// 행 이동 처리
let itemToMove = items[(fromIndexPath as NSIndexPath).row]
let itemImageToMove = itemsImageFile[(fromIndexPath as NSIndexPath).row]
items.remove(at: (fromIndexPath as NSIndexPath).row)
itemsImageFile.remove(at: (fromIndexPath as NSIndexPath).row)
items.insert(itemToMove, at: (to as NSIndexPath).row)
itemsImageFile.insert(itemImageToMove, at: (to as NSIndexPath).row)
}
// Override to support conditional rearranging of the table view.
// 행 이동 가능 여부 설정
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
returntrue
}
// MARK: - Navigation
// 내비게이션을 위한 준비 작업
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// segue.identifier가 "sgDetail"인 경우
if segue.identifier == "sgDetail" {
let cell = sender as! UITableViewCell // 선택된 셀
let indexPath = self.tvListView.indexPath(for: cell) // 셀의 인덱스 경로
let detailView = segue.destination as! DetailViewController // 목적지 뷰 컨트롤러
detailView.reciveItem(items[((indexPath! as NSIndexPath).row)]) // 선택된 아이템 전달
}
}
}
iOS 앱을 만들 때 가장 많이 사용하는 UI 요소
1. UILabel: 텍스트를 표시하는 데 사용됩니다. 주로 제목이나 설명을 나타낼 때 사용됩니다.
2. UIButton: 사용자와의 상호작용을 위한 버튼입니다. 다양한 스타일과 동작을 정의할 수 있습니다.
3. UIImageView: 이미지를 표시하는 데 사용됩니다. 사진이나 아이콘 등 다양한 이미지를 표시할 수 있습니다.
4. UITableView: 리스트 형태로 데이터를 표시할 때 사용됩니다. 스크롤이 가능한 목록을 만들 수 있습니다.
13 음악 재생하고 녹음하기
-
AVAudioPlayer 사용하기 위해선
import AVFoundation추가해야됨
import UIKit
import AVFoundation
class ViewController: UIViewController, AVAudioPlayerDelegate, AVAudioRecorderDelegate {
var audioPlayer: AVAudioPlayer! // 오디오 재생을 위한 AVAudioPlayer
var audioFile: URL! // 오디오 파일 URL
let MAX_VOLUME: Float = 10.0 // 최대 볼륨 값
var progressTimer: Timer! // 재생 진행 상태를 업데이트하기 위한 타이머
// 타이머 업데이트 메서드를 위한 셀렉터
let timePlayerSelector: Selector = #selector(ViewController.updatePlayTime)let timeRecordSelector: Selector = #selector(ViewController.updateRecordTime)
// UI 아울렛 변수들
@IBOutlet var pvProgressPlay: UIProgressView! // 재생 진행 상태를 표시하는 프로그레스 뷰
@IBOutlet var lblCurrentTime: UILabel! // 현재 재생 시간 레이블
@IBOutlet var lblEndTime: UILabel! // 총 재생 시간 레이블
@IBOutlet var btnPlay: UIButton! // 재생 버튼
@IBOutlet var btnPause: UIButton! // 일시 정지 버튼
@IBOutlet var btnStop: UIButton! // 정지 버튼
@IBOutlet var slVolume: UISlider! // 볼륨 조절 슬라이더
@IBOutlet var btnRecord: UIButton! // 녹음 버튼
@IBOutlet var lblRecordTime: UILabel! // 녹음 시간 레이블
var audioRecorder: AVAudioRecorder! // 오디오 녹음을 위한 AVAudioRecorder
var isRecordMode = false // 녹음 모드 여부
override func viewDidLoad() {
super.viewDidLoad()
// 뷰 로드 후 추가 설정
selectAudioFile() // 오디오 파일 선택
if !isRecordMode {
initPlay() // 재생 초기화
btnRecord.isEnabled = false // 녹음 버튼 비활성화
lblRecordTime.isEnabled = false // 녹음 시간 레이블 비활성화
} else {
initRecord() // 녹음 초기화
}
}
func selectAudioFile() {
// 녹음 모드에 따라 오디오 파일 선택
if !isRecordMode {
audioFile = Bundle.main.url(forResource: "Sicilian_Breeze", withExtension: "mp3") // 재생할 파일
} else {
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
audioFile = documentDirectory.appendingPathComponent("recordFile.m4a") // 녹음 파일 경로
}
}
func initRecord() {
// 녹음을 위한 설정
let recordSettings = [
AVFormatIDKey: NSNumber(value: kAudioFormatAppleLossless as UInt32),
AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue,
AVEncoderBitRateKey: 320000,
AVNumberOfChannelsKey: 2,
AVSampleRateKey: 44100.0
] as [String: Any]
do {
audioRecorder = try AVAudioRecorder(url: audioFile, settings: recordSettings) // AVAudioRecorder 초기화
} catch let error as NSError {
print("Error-initRecord: \(error)") // 오류 처리
}
audioRecorder.delegate = self // 델리게이트 설정
slVolume.value = 1.0 // 기본 볼륨 설정
audioPlayer.volume = slVolume.value // 재생기 볼륨 설정
lblEndTime.text = convertNSTimeInterval2String(0) // 종료 시간 초기화
lblCurrentTime.text = convertNSTimeInterval2String(0) // 현재 시간 초기화
setPlayButtons(false, pause: false, stop: false) // 버튼 상태 설정
let session = AVAudioSession.sharedInstance() // 오디오 세션 생성
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default) // 카테고리 설정
try AVAudioSession.sharedInstance().setActive(true) // 세션 활성화
} catch let error as NSError {
print("Error-setCategory: \(error)") // 오류 처리
}
}
func initPlay() {
// 재생을 위한 초기 설정
do {
audioPlayer = try AVAudioPlayer(contentsOf: audioFile) // AVAudioPlayer 초기화
} catch let error as NSError {
print("Error-initPlay: \(error)") // 오류 처리
}
slVolume.maximumValue = MAX_VOLUME // 슬라이더 최대값 설정
slVolume.value = 1.0 // 기본 볼륨 설정
pvProgressPlay.progress = 0 // 프로그레스 뷰 초기화
audioPlayer.delegate = self // 델리게이트 설정
audioPlayer.prepareToPlay() // 재생 준비
audioPlayer.volume = slVolume.value // 볼륨 설정
lblEndTime.text = convertNSTimeInterval2String(audioPlayer.duration) // 종료 시간 표시
lblCurrentTime.text = convertNSTimeInterval2String(0) // 현재 시간 초기화
setPlayButtons(true, pause: false, stop: false) // 버튼 상태 설정
}
func setPlayButtons(_ play: Bool, pause: Bool, stop: Bool) {
// 버튼 활성화 상태 설정
btnPlay.isEnabled = play
btnPause.isEnabled = pause
btnStop.isEnabled = stop
}
func convertNSTimeInterval2String(_ time: TimeInterval) -> String {
// NSTimeInterval을 문자열로 변환
let min = Int(time / 60)
let sec = Int(time.truncatingRemainder(dividingBy: 60))
let strTime = String(format: "%02d:%02d", min, sec)
return strTime
}
@IBAction func btnPlayAudio(_ sender: UIButton) {
// 재생 버튼 클릭 시 호출
audioPlayer.play() // 오디오 재생
setPlayButtons(false, pause: true, stop: true) // 버튼 상태 설정
progressTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: timePlayerSelector, userInfo: nil, repeats: true) // 타이머 시작
}
@objc func updatePlayTime() {
// 재생 시간 업데이트
lblCurrentTime.text = convertNSTimeInterval2String(audioPlayer.currentTime) // 현재 시간 레이블 업데이트
pvProgressPlay.progress = Float(audioPlayer.currentTime / audioPlayer.duration) // 프로그레스 뷰 업데이트
}
@IBAction func btnPauseAudio(_ sender: UIButton) {
// 일시 정지 버튼 클릭 시 호출
audioPlayer.pause() // 오디오 일시 정지
setPlayButtons(true, pause: false, stop: true) // 버튼 상태 설정
}
@IBAction func btnStopAudio(_ sender: UIButton) {
// 정지 버튼 클릭 시 호출
audioPlayer.stop() // 오디오 정지
audioPlayer.currentTime = 0 // 재생 시간 초기화
lblCurrentTime.text = convertNSTimeInterval2String(0) // 현재 시간 레이블 초기화
setPlayButtons(true, pause: false, stop: false) // 버튼 상태 설정
progressTimer.invalidate() // 타이머 중지
}
@IBAction func slChangeVolume(_ sender: UISlider) {
// 볼륨 슬라이더 값 변경 시 호출
audioPlayer.volume = slVolume.value // 오디오 볼륨 설정
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
// 오디오 재생 완료 시 호출
progressTimer.invalidate() // 타이머 중지
setPlayButtons(true, pause: false, stop: false) // 버튼 상태 설정
}
@IBAction func swRecordMode(_ sender: UISwitch) {
// 녹음 모드 스위치 변경 시 호출
if sender.isOn {
audioPlayer.stop() // 재생 중지
audioPlayer.currentTime = 0 // 재생 시간 초기화
lblRecordTime.text = convertNSTimeInterval2String(0) // 녹음 시간 초기화
isRecordMode = true // 녹음 모드 활성화
btnRecord.isEnabled = true // 녹음 버튼 활성화
lblRecordTime.isEnabled = true // 녹음 시간 레이블 활성화
} else {
isRecordMode = false // 녹음 모드 비활성화
btnRecord.isEnabled = false // 녹음 버튼 비활성화
lblRecordTime.isEnabled = false // 녹음 시간 레이블 비활성화
lblRecordTime.text = convertNSTimeInterval2String(0) // 녹음 시간 초기화
}
selectAudioFile() // 오디오 파일 선택
if !isRecordMode {
initPlay() // 재생 초기화
} else
14 비디오 재생 앱 만들기
//
// ViewController.swift
// MoviePlayer
//
// Created by Ho-Jeong Song on 2021/11/26.
//
import UIKit
import AVKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 뷰가 로드된 후 추가 설정
}
@IBAction func btnPlayInternalMovie(_ sender: UIButton) {
// 내부 파일 mp4 재생
let filePath: String? = Bundle.main.path(forResource: "FastTyping", ofType: "mp4") // 내부 파일 경로
let url = NSURL(fileURLWithPath: filePath!) // NSURL 객체 생성
playVideo(url: url) // 비디오 재생 함수 호출
}
@IBAction func btnPlayerExternalMovie(_ sender: UIButton) {
// 외부 파일 mp4 재생
let url = NSURL(string: "https://dl.dropboxusercontent.com/s/e38auz050w2mvud/Fireworks.mp4")! // 외부 URL 생성
playVideo(url: url) // 비디오 재생 함수 호출
}
private func playVideo(url: NSURL) {
// 비디오 재생을 위한 함수
let playerController = AVPlayerViewController() // 비디오 플레이어 컨트롤러 생성
let player = AVPlayer(url: url as URL) // AVPlayer 객체 생성
playerController.player = player // 플레이어를 플레이어 컨트롤러에 설정
self.present(playerController, animated: true) {
player.play() // 비디오 재생 시작
}
}
}
17 탭과 터치 사용해 스케치 앱 만들기
import UIKit
class ViewController: UIViewController {
@IBOutlet var imgView: UIImageView! // 그림을 그릴 이미지 뷰
var lastPoint: CGPoint! // 마지막 터치 위치
var lineSize: CGFloat = 2.0 // 선의 두께
var lineColor = UIColor.black.cgColor // 선의 색상
override func viewDidLoad() {
super.viewDidLoad()
// 뷰가 로드된 후 추가 설정
}
@IBAction func btnClearImageView(_ sender: UIButton) {
// 이미지 뷰를 지우는 버튼 액션
imgView.image = nil // 이미지 뷰의 이미지를 nil로 설정
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 시작될 때 호출
let touch = touches.first! as UITouch // 첫 번째 터치 가져오기
lastPoint = touch.location(in: imgView) // 마지막 터치 위치 설정
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 이동할 때 호출
UIGraphicsBeginImageContext(imgView.frame.size) // 새로운 그래픽 컨텍스트 시작
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor) // 선 색상 설정
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round) // 선 끝 모양 설정
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize) // 선 두께 설정
let touch = touches.first! as UITouch // 첫 번째 터치 가져오기
let currPoint = touch.location(in: imgView) // 현재 터치 위치
// 현재 이미지를 그래픽 컨텍스트에 그리기
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 선을 그리기
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y)) // 시작점 설정
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: currPoint.x, y: currPoint.y)) // 끝점 설정
UIGraphicsGetCurrentContext()?.strokePath() // 선 그리기
imgView.image = UIGraphicsGetImageFromCurrentImageContext() // 그린 이미지를 이미지 뷰에 설정
UIGraphicsEndImageContext() // 그래픽 컨텍스트 종료
lastPoint = currPoint // 마지막 위치 업데이트
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 끝날 때 호출
UIGraphicsBeginImageContext(imgView.frame.size) // 새로운 그래픽 컨텍스트 시작
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor) // 선 색상 설정
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round) // 선 끝 모양 설정
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize) // 선 두께 설정
// 현재 이미지를 그래픽 컨텍스트에 그리기
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 마지막 터치 위치에서 선 그리기
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: lastPoint.x, y: lastPoint.y)) // 마지막 점에서 자신으로 선을 그리기
UIGraphicsGetCurrentContext()?.strokePath() // 선 그리기
imgView.image = UIGraphicsGetImageFromCurrentImageContext() // 그린 이미지를 이미지 뷰에 설정
UIGraphicsEndImageContext() // 그래픽 컨텍스트 종료
}
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
// 흔들림 제스처가 끝날 때 호출
if motion == .motionShake {
imgView.image = nil // 흔들림 제스처로 이미지 뷰 지우기
}
}
}
//
// ViewController.swift
// Sketch
//
// Created by Ho-Jeong Song on 2021/12/01.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet var imgView: UIImageView! // 그림을 그릴 이미지 뷰
@IBOutlet var txtLineSize: UITextField! // 선의 두께를 입력받는 텍스트 필드
var lastPoint: CGPoint! // 마지막 터치 위치
var lineSize: CGFloat = 2.0 // 선의 기본 두께
var lineColor = UIColor.red.cgColor // 선의 기본 색상
override func viewDidLoad() {
super.viewDidLoad()
// 뷰가 로드된 후 추가 설정
txtLineSize.text = String(Int(lineSize)) // 현재 선 두께를 텍스트 필드에 표시
}
@IBAction func btnClearImageView(_ sender: UIButton) {
// 이미지 뷰를 지우는 버튼 액션
imgView.image = nil // 이미지 뷰의 이미지를 nil로 설정
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 시작될 때 호출
let touch = touches.first! as UITouch // 첫 번째 터치 가져오기
lastPoint = touch.location(in: imgView) // 마지막 터치 위치 설정
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 이동할 때 호출
UIGraphicsBeginImageContext(imgView.frame.size) // 새로운 그래픽 컨텍스트 시작
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor) // 선 색상 설정
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round) // 선 끝 모양 설정
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize) // 선 두께 설정
let touch = touches.first! as UITouch // 첫 번째 터치 가져오기
let currPoint = touch.location(in: imgView) // 현재 터치 위치
// 이미지 뷰의 현재 내용을 그래픽 컨텍스트에 그리기
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 선 그리기
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y)) // 시작점 설정
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: currPoint.x, y: currPoint.y)) // 끝점 설정
UIGraphicsGetCurrentContext()?.strokePath() // 선 그리기
imgView.image = UIGraphicsGetImageFromCurrentImageContext() // 그린 이미지를 이미지 뷰에 설정
UIGraphicsEndImageContext() // 그래픽 컨텍스트 종료
lastPoint = currPoint // 마지막 위치 업데이트
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// 터치가 끝날 때 호출
UIGraphicsBeginImageContext(imgView.frame.size) // 새로운 그래픽 컨텍스트 시작
UIGraphicsGetCurrentContext()?.setStrokeColor(lineColor) // 선 색상 설정
UIGraphicsGetCurrentContext()?.setLineCap(CGLineCap.round) // 선 끝 모양 설정
UIGraphicsGetCurrentContext()?.setLineWidth(lineSize) // 선 두께 설정
// 이미지 뷰의 현재 내용을 그래픽 컨텍스트에 그리기
imgView.image?.draw(in: CGRect(x: 0, y: 0, width: imgView.frame.size.width, height: imgView.frame.size.height))
// 마지막 터치 위치에서 선 그리기
UIGraphicsGetCurrentContext()?.move(to: CGPoint(x: lastPoint.x, y: lastPoint.y))
UIGraphicsGetCurrentContext()?.addLine(to: CGPoint(x: lastPoint.x, y: lastPoint.y)) // 마지막 점에서 자신으로 선 그리기
UIGraphicsGetCurrentContext()?.strokePath() // 선 그리기
imgView.image = UIGraphicsGetImageFromCurrentImageContext() // 그린 이미지를 이미지 뷰에 설정
UIGraphicsEndImageContext() // 그래픽 컨텍스트 종료
}
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
// 흔들림 제스처가 끝날 때 호출
if motion == .motionShake {
imgView.image = nil // 흔들림 제스처로 이미지 뷰 지우기
}
}
@IBAction func txtEditChange(_ sender: UITextField) {
// 텍스트 필드의 내용이 변경될 때 호출
if txtLineSize.text != "" {
lineSize = CGFloat(Int(txtLineSize.text!)!) // 입력된 값을 선 두께로 설정
}
}
@IBAction func txtDidEndOnExit(_ sender: UITextField) {
// 텍스트 필드에서 입력이 끝날 때 호출
lineSize = CGFloat(Int(txtLineSize.text!)!) // 입력된 값을 선 두께로 설정
}
@IBAction func txtTouchDown(_ sender: UITextField) {
// 텍스트 필드가 터치될 때 호출
txtLineSize.selectAll(UITextField.self) // 텍스트 필드의 모든 텍스트 선택
}
@IBAction func btnChangeLineColorBlack(_ sender: UIButton) {
// 검은색 버튼 클릭 시 호출
lineColor = UIColor.black.cgColor // 선 색상을 검은색으로 설정
}
@IBAction func btnChangeLineColorRed(_ sender: UIButton) {
// 빨간색 버튼 클릭 시 호출
lineColor = UIColor.red.cgColor // 선 색상을 빨간색으로 설정
}
@IBAction func btnChangeLineColorGreen(_ sender: UIButton) {
// 초록색 버튼 클릭 시 호출
lineColor = UIColor.green.cgColor // 선 색상을 초록색으로 설정
}
@IBAction func btnChangeLineColorBlue(_ sender: UIButton) {
// 파란색 버튼 클릭 시 호출
lineColor = UIColor.blue.cgColor // 선 색상을 파란색으로 설정
}
}
BMI 앱 기획서 1. 앱 개요 1.1 앱 이름 HealthCalc: BMI Calculator 1.2 앱 설명 HealthCalc는 사용자가 자신의 BMI(체질량지수)를 쉽게 계산하고, 건강을 관리하는 데 도움을 주는 앱입니다. 사용자 친화적인 인터페이스와 다양한 기능을 통해 체중 관리와 건강한 생활 습관을 촉진합니다. 2. 주요 기능 2.1 사용자 입력 **키(cm)**와 체중(kg) 입력을 위한 텍스트 필드 제공 입력 유효성 검사: 숫자가 아닌 경우 오류 메시지 표시 2.2 BMI 계산 사용자가 입력한 값으로 BMI를 계산하고, 결과를 실시간으로 업데이트 계산된 BMI 값에 따라 건강 상태 메시지 제공 (예: 저체중, 정상 체중, 과체중, 비만) 2.3 결과 저장 및 기록 사용자의 BMI 계산 기록을 저장하고, 이전 기록을 확인할 수 있는 기능 제공 기록된 데이터를 기반으로 그래프 또는 차트로 시각화하여 건강 변화 추적 가능 2.4 추가 기능 건강 팁: BMI에 따라 맞춤형 건강 팁 제공 (예: 운동 추천, 식단 조절) 목표 설정: 사용자가 목표 체중을 설정하고, 이를 달성하기 위한 계획 수립 알림 기능: 정기적으로 체중 측정을 하도록 알림 설정 2.5 다국어 지원 다양한 언어로 앱을 제공하여 글로벌 사용자층 확보 3. UI/UX 디자인 3.1 디자인 원칙 심플하고 직관적인 UI: 사용자가 쉽게 이해하고 사용할 수 있도록 간결한 디자인 일관된 색상 및 폰트: 건강과 관련된 차분한 색상 사용 (예: 초록색, 파란색) 3.2 화면 구성 홈 화면: 키와 체중 입력 필드, 계산 버튼, 결과 표시 영역 결과 화면: BMI 결과, 건강 상태 메시지, 이전 기록 버튼 기록 화면: 과거 BMI 기록 목록, 그래프 표시 설정 화면: 알림 설정, 언어 선택, 도움말 4. 경쟁 분석 4.1 기존 인기 앱 분석 MyFitnessPal: 체중 관리와 영양 추적에 강점을 가진 앱. 다양한 기능과 사용자 커뮤니티 제공. BMI Calculator: 간단하고 직관적인 BMI 계산 기능. 사용자 친화적인 인터페이스. Lose It!: 체중 감량 목표 설정 및 추적 기능 제공. 사용자 맞춤형 다이어트 계획. 4.2 차별점 HealthCalc는 BMI 계산뿐만 아니라, 사용자 맞춤형 건강 팁과 목표 설정 기능을 통해 보다 포괄적인 건강 관리 솔루션을 제공합니다. 5. 마케팅 전략 5.1 타겟 사용자 건강 관리를 원하는 일반 사용자 체중 감량 및 운동을 목표로 하는 사용자 5.2 홍보 전략 소셜 미디어 플랫폼을 통한 광고 및 캠페인 건강 관련 블로그 및 웹사이트와의 협업 사용자 후기를 통한 신뢰성 확보 6. 개발 계획 6.1 기술 스택 프론트엔드: Swift, UIKit 백엔드: Firebase (데이터 저장 및 사용자 인증) 디자인 도구: Figma 또는 Sketch 6.2 개발 일정 1개월: 기획 및 디자인 2개월: 개발 및 내부 테스트 1개월: 베타 테스트 및 피드백 반영 1개월: 출시 준비 및 마케팅 7. 예상 비용 개발 인력, 디자인, 마케팅 비용 등을 포함한 예산 계획 수립
1. ui디자인
2.outlet
3.action
4. connections inspector로 outlet이나 action 이 한번만 연결된 것인지 확인
5.action에 소스코드 작성
이미지는 assets.xcassets에 넣고 alt키를 눌러서 옮기는 이유는 선명하게(?)하기 위해
Decimal pad
출처:
Do it! 스위프트로 아이폰 앱 만들기 입문, 송호정, 이범근 저,이지스퍼블리싱, 2023년 01월 20일
---------------------------------------- 02 Hello World 앱 만들며 Xcode에 완벽 적응하기 03 원하는 이미지 화면에 출력하기 - 이미지 뷰 04 데이트 피커 사용해 날짜 선택하기 05 피커 뷰 사용해 원하는 항목 선택하기 06 얼럿 사용해 경고 표시하기 07 웹 뷰로 간단한 웹 브라우저 만들기 08 맵 뷰로 지도 나타내기 09 페이지 이동하기 - 페이지 컨트롤 10 탭 바 컨트롤러 이용해 여러 개의 뷰 넣기 11 내비게이션 컨트롤러 이용해 화면 전환하기 12 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기 13 음악 재생하고 녹음하기 14 비디오 재생 앱 만들기 15 카메라와 포토 라이브러리에서 미디어 가져오기 16 코어 그래픽스로 화면에 그림 그리기 17 탭과 터치 사용해 스케치 앱 만들기 18 스와이프 제스처 사용하기 19 핀치 제스처 사용해 사진을 확대/축소하기
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// UILabel 생성
let myLabel = UILabel()
// 레이블 속성 설정
myLabel.text = "안녕하세요, iOS!"
myLabel.textColor = .black
myLabel.font = UIFont.systemFont(ofSize: 24)
myLabel.textAlignment = .center
// 레이블의 크기와 위치 설정
myLabel.frame = CGRect(x: 0, y: 0, width: 300, height: 50)
myLabel.center = view.center // 화면 중앙에 배치
// 레이블을 뷰에 추가
view.addSubview(myLabel)
}
}
aspect fit으로 하는게 좋음
import UIKit
class ViewController: UIViewController {
var x :Int = 0
@IBOutlet weak var labelnumber: UILabel!
@IBOutlet weak var lblHello: UILabel!
@IBAction func btnDown(_ sender: UIButton) {
x = x-1 //마이너스 버튼 누르면 숫자가 줄어든다
labelnumber.text = String(x)
}
@IBAction func btnUp(_ sender: UIButton) {
x = x+1//플러스 버튼 누르면 S가 숫자로 바뀌고 숫자가 늘어남
labelnumber.text = String(x)
}
@IBOutlet weak var txtName: UITextField!
@IBAction func btnSend(_ sender: UIButton) {
lblHello.text = "Hi, " + txtName.text!
print(lblHello.text, txtName.text)
}
@IBAction func btnReset(_ sender: UIButton) {
lblHello.textColor = .systemBlue
lblHello.text = "안녕하세요!"
txtName.text = ""
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
03 원하는 이미지 화면에 출력하기 - 이미지 뷰
아래는 주어진 Swift 코드에 대한 주석입니다. 각 부분의 기능을 설명합니다.
```swift
//
// ViewController.swift
// ImageView
//
// Created by Ho-Jeong Song on 2021/11/23.
//
import UIKit
// ViewController 클래스 정의
class ViewController: UIViewController {
// 이미지 확대 여부를 나타내는 변수
var isZoom = false
// 이미지 온, 오프 상태에 대한 UIImage 변수
var imgOn: UIImage?
var imgOff: UIImage?
// IBOutlet으로 연결된 UIImageView와 UIButton
@IBOutlet var imgView: UIImageView!
@IBOutlet var btnResize: UIButton!
// 뷰가 로드될 때 호출되는 메서드
override func viewDidLoad() {
super.viewDidLoad()
// 이미지 파일을 로드하여 변수에 저장
imgOn = UIImage(named: "lamp_on.png")
imgOff = UIImage(named: "lamp_off.png")
// 초기 이미지로 imgOn 설정
imgView.image = imgOn
}
// UIButton 클릭 시 호출되는 액션 메서드
@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일 경우 (이미지가 축소된 상태)
newWidth = imgView.frame.width * scale // 너비 확대
newHeight = imgView.frame.height * scale // 높이 확대
// 버튼의 제목을 "축소"로 변경
btnResize.setTitle("축소", for: .normal)
}
// 이미지 뷰의 크기를 새로운 크기로 설정
imgView.frame.size = CGSize(width: newWidth, height: newHeight)
// isZoom 상태를 반전시켜 다음 클릭 시 동작 변경
isZoom = !isZoom
}
// UISwitch 클릭 시 호출되는 액션 메서드
@IBAction func switchImageOnOff(_ sender: UISwitch) {
// 스위치가 켜져 있으면 imgOn 이미지 설정
if sender.isOn {
imgView.image = imgOn
} else { // 스위치가 꺼져 있으면 imgOff 이미지 설정
imgView.image = imgOff
}
}
}
```
### 주석 요약
- 이 코드는 iOS 앱의 ViewController를 정의하고, 이미지 뷰와 버튼, 스위치를 통해 이미지를 확대/축소하거나 변경하는 기능을 제공합니다.
- `viewDidLoad` 메서드는 뷰가 로드될 때 초기 설정을 담당합니다.
- `btnResizeImage` 메서드는 버튼 클릭 시 이미지를 확대하거나 축소합니다.
- `switchImageOnOff` 메서드는 스위치의 상태에 따라 이미지를 변경합니다.
이런 자료를 참고했어요.
[1] velog - [Swift] ViewController 화면전환 방법 (https://velog.io/@5n_tak/Swift-ViewController-%ED%99%94%EB%A9%B4%EC%A0%84%ED%99%98-%EB%B0%A9%EB%B2%95)
[2] 티스토리 - [iOS/Swift] ViewController 화면 전환 방법 3가지 (https://eunoia3jy.tistory.com/210)
[3] 티스토리 - [iOS/swift] 이미지 뷰 (ImageView) 앱 만들기 - 무니봇 IT 블로그 (https://moonibot.tistory.com/4)
[4] 네이버 블로그 - [iOS] ViewController의 특징과 생명주기 - 네이버 블로그 (https://m.blog.naver.com/doctor-kick/222424663918)
뤼튼 사용하러 가기 > https://agent.wrtn.ai/5xb91l
04 데이트 피커 사용해 날짜 선택하기
06 얼럿 사용해 경고 표시하기
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 var lampImg: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
lampImg.image = imgOn
}
@IBAction func btnLampOn(_ sender: UIButton) {
if(isLampOn==true) {
let lampOnAlert = UIAlertController(title: "경고", message: "현재 On 상태입니다", preferredStyle: UIAlertController.Style.alert)
let onAction = UIAlertAction(title: "네, 알겠습니다.", style: UIAlertAction.Style.default, handler: nil)
lampOnAlert.addAction(onAction)
present(lampOnAlert, animated: true, completion: nil)
} else {
lampImg.image = imgOn
isLampOn=true
}
}
@IBAction func btnLanpOff(_ sender: UIButton) {
if isLampOn {
let lampOffAlert = UIAlertController(title: "램프 끄기", message: "램프를 끄시겠습니까?", preferredStyle: UIAlertController.Style.alert)
let offAction = UIAlertAction(title: "네", style: UIAlertAction.Style.default, handler: {
ACTION in self.lampImg.image = self.imgOff
self.isLampOn=false
})
let cancelAction = UIAlertAction(title: "아니오", style: UIAlertAction.Style.default, handler: nil)
lampOffAlert.addAction(offAction)
lampOffAlert.addAction(cancelAction)
present(lampOffAlert, animated: true, completion: nil)
}
}
@IBAction func btnLampRemove(_ sender: UIButton) {
let lampRemoveAlert = UIAlertController(title: "램프 제거", message: "램프를 제거하시겠습니까?", preferredStyle: UIAlertController.Style.alert)
let offAction = UIAlertAction(title: "아니오, 끕니다(off).", style: UIAlertAction.Style.default, handler: {
ACTION in self.lampImg.image = self.imgOff
self.isLampOn=false
})
let onAction = UIAlertAction(title: "아니오, 켭니다(on).", style: UIAlertAction.Style.default) {
ACTION in self.lampImg.image = self.imgOn
self.isLampOn=true
}
let removeAction = UIAlertAction(title: "네, 제거합니다.", style: UIAlertAction.Style.destructive, handler: {
ACTION in self.lampImg.image = self.imgRemove
self.isLampOn=false
})
lampRemoveAlert.addAction(offAction)
lampRemoveAlert.addAction(onAction)
lampRemoveAlert.addAction(removeAction)
present(lampRemoveAlert, animated: true, completion: nil)
}
}
import UIKit // UIKit 프레임워크를 임포트하여 UI 관련 기능을 사용할 수 있도록 함
import MapKit // MapKit 프레임워크를 임포트하여 지도 관련 기능을 사용할 수 있도록 함
class ViewController: UIViewController, CLLocationManagerDelegate {
// UIViewController를 상속받고, CLLocationManagerDelegate 프로토콜을 채택
@IBOutlet var myMap: MKMapView! // 지도 뷰를 연결하는 아울렛
@IBOutlet var lblLocationInfo1: UILabel! // 위치 정보를 표시할 레이블 1
@IBOutlet var lblLocationInfo2: UILabel! // 위치 정보를 표시할 레이블 2
let locationManager = CLLocationManager() // 위치 관리 객체 생성
override func viewDidLoad() {
super.viewDidLoad() // 부모 클래스의 viewDidLoad 호출
// 뷰가 로드된 후 추가 설정을 수행
lblLocationInfo1.text = "" // 레이블 1 초기화
lblLocationInfo2.text = "" // 레이블 2 초기화
locationManager.delegate = self // 위치 관리자의 델리게이트를 현재 뷰 컨트롤러로 설정
locationManager.desiredAccuracy = kCLLocationAccuracyBest // 위치 정확도를 최대로 설정
locationManager.requestWhenInUseAuthorization() // 앱 사용 중 위치 권한 요청
locationManager.startUpdatingLocation() // 위치 업데이트 시작
myMap.showsUserLocation = true // 사용자 위치 표시 활성화
}
func goLocation(latitudeValue: CLLocationDegrees, longitudeValue: CLLocationDegrees, delta span: Double) -> CLLocationCoordinate2D {
let pLocation = CLLocationCoordinate2DMake(latitudeValue, longitudeValue) // 위도와 경도로 CLLocationCoordinate2D 객체 생성
let spanValue = MKCoordinateSpan(latitudeDelta: span, longitudeDelta: span) // 줌 레벨을 설정하는 span 객체 생성
let pRegion = MKCoordinateRegion(center: pLocation, span: spanValue) // 중심과 줌 레벨로 MKCoordinateRegion 객체 생성
myMap.setRegion(pRegion, animated: true) // 지도의 지역을 설정하고 애니메이션 효과 적용
return pLocation // 생성한 위치 반환
}
func setAnnotation(latitudeValue: CLLocationDegrees, longitudeValue: CLLocationDegrees, delta span: Double, title strTitle: String, subtitle strSubtitle: String) {
let annotation = MKPointAnnotation() // 새 주석(annotation) 객체 생성
annotation.coordinate = goLocation(latitudeValue: latitudeValue, longitudeValue: longitudeValue, delta: span) // 주석의 좌표 설정
annotation.title = strTitle // 주석의 제목 설정
annotation.subtitle = strSubtitle // 주석의 부제 설정
myMap.addAnnotation(annotation) // 지도에 주석 추가
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let pLocation = locations.last // 가장 최근의 위치 정보 가져오기
_ = goLocation(latitudeValue: (pLocation?.coordinate.latitude)!, longitudeValue: (pLocation?.coordinate.longitude)!, delta: 0.01) // 위치를 지도에 설정
CLGeocoder().reverseGeocodeLocation(pLocation!, completionHandler: { (placemarks, error) -> Void in // 역 지오코딩을 통해 주소 정보 가져오기
let pm = placemarks!.first // 첫 번째 placemark 가져오기
let country = pm!.country // 국가 정보 가져오기
var address: String = country! // 주소 초기화 (국가 포함)
if pm!.locality != nil { // 지역 정보가 있는 경우
address += " " // 공백 추가
address += pm!.locality! // 지역 정보 추가
}
if pm!.thoroughfare != nil { // 도로 정보가 있는 경우
address += " " // 공백 추가
address += pm!.thoroughfare! // 도로 정보 추가
}
self.lblLocationInfo1.text = "현재 위치" // 레이블 1에 현재 위치 텍스트 설정
self.lblLocationInfo2.text = address // 레이블 2에 주소 텍스트 설정
})
locationManager.stopUpdatingLocation() // 위치 업데이트 중지
}
@IBAction func sgChangeLocation(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 { // 첫 번째 세그먼트 선택 시
self.lblLocationInfo1.text = "" // 레이블 1 초기화
self.lblLocationInfo2.text = "" // 레이블 2 초기화
locationManager.startUpdatingLocation() // 위치 업데이트 시작
} elseif sender.selectedSegmentIndex == 1 { // 두 번째 세그먼트 선택 시
setAnnotation(latitudeValue: 37.63207, longitudeValue: 127.0554, delta: 0.01, title: "인덕대학", subtitle: "노원구 월계동") // 인덕대학 주석 추가
self.lblLocationInfo1.text = "보고 계신 위치" // 레이블 1에 텍스트 설정
self.lblLocationInfo2.text = "한국폴리텍대학 강릉캠퍼스" // 레이블 2에 텍스트 설정
} elseif sender.selectedSegmentIndex == 2 { // 세 번째 세그먼트 선택 시
setAnnotation(latitudeValue: 37.556876, longitudeValue: 126.914066, delta: 0.1, title: "이지스퍼블리싱", subtitle: "서울시 마포구 잔다리로 109 이지스 빌딩") // 이지스퍼블리싱 주석 추가
self.lblLocationInfo1.text = "보고 계신 위치" // 레이블 1에 텍스트 설정
self.lblLocationInfo2.text = "이지스퍼블리싱 출판사 " // 레이블 2에 텍스트 설정
}
}
}
출처:
Do it! 스위프트로 아이폰 앱 만들기 입문, 송호정, 이범근 저,이지스퍼블리싱, 2023년 01월 20일
---------------------------------------- 02 Hello World 앱 만들며 Xcode에 완벽 적응하기 03 원하는 이미지 화면에 출력하기 - 이미지 뷰 04 데이트 피커 사용해 날짜 선택하기 05 피커 뷰 사용해 원하는 항목 선택하기 06 얼럿 사용해 경고 표시하기 07 웹 뷰로 간단한 웹 브라우저 만들기 08 맵 뷰로 지도 나타내기 09 페이지 이동하기 - 페이지 컨트롤 10 탭 바 컨트롤러 이용해 여러 개의 뷰 넣기 11 내비게이션 컨트롤러 이용해 화면 전환하기 12 테이블 뷰 컨트롤러 이용해 할 일 목록 만들기 13 음악 재생하고 녹음하기 14 비디오 재생 앱 만들기 15 카메라와 포토 라이브러리에서 미디어 가져오기 16 코어 그래픽스로 화면에 그림 그리기 17 탭과 터치 사용해 스케치 앱 만들기 18 스와이프 제스처 사용하기 19 핀치 제스처 사용해 사진을 확대/축소하기
import UIKit // UIKit 프레임워크를 임포트하여 iOS 사용자 인터페이스를 구성할 수 있게 함
class ViewController: UIViewController { // ViewController라는 이름의 클래스를 정의하며, UIViewController를 상속받음
override func viewDidLoad() { // 뷰가 메모리에 로드된 후 호출되는 메서드를 오버라이드
super.viewDidLoad() // 부모 클래스의 viewDidLoad 메서드를 호출하여 기본 설정을 수행
// Do any additional setup after loading the view. // 뷰 로딩 후 추가적인 설정을 할 수 있는 부분 (현재는 비어 있음)
}
}
outlet변수 추가하는 방법
연결되어있는 거 끄기(연결이 중복돼 있는 경우) 경고 표시 있는 거 지우기
1. 기본 UILabel 생성 및 텍스트 설정
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// UILabel 생성
let myLabel = UILabel()
myLabel.text = "안녕하세요!" // 텍스트 설정
myLabel.textColor = .black // 텍스트 색상 설정
myLabel.font = UIFont.systemFont(ofSize: 24) // 글꼴 크기 설정
myLabel.textAlignment = .center // 텍스트 정렬 설정
// UILabel의 프레임 설정
myLabel.frame = CGRect(x: 50, y: 100, width: 300, height: 50)
// 뷰에 UILabel 추가
self.view.addSubview(myLabel)
}
}
2. UILabel을 IBOutlet으로 연결하여 텍스트 업데이트
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel! // 스토리보드에서 연결된 UILabel
override func viewDidLoad() {
super.viewDidLoad()
// UILabel의 텍스트 업데이트
myLabel.text = "안녕하세요, Swift!" // 텍스트 설정
}
}
위의 예제에서 각 언어는 Animal 클래스를 기본 클래스로 하고, Dog 클래스가 이를 상속받아 makeSound 또는 make_sound 메서드를 오버라이드하여 개의 소리를 출력하는 방법을 보여줍니다.
에러 원인: class 안에 있는 stored property는 반드시 초기값이 있어야한다
해결방법>
1.초기값 직접 지정하는 방법
var x : Int
class Man{
var age : Int = 0
var weight : Double = 0.0
}
2. 옵셔널로 만드는 방법(자동으로 nill 초기화됨)
var x : Int
class Man{
var age : Int? //nill로 초기화 되어 있는거임
var weight : Double?
}
인스턴스 만들고 메서드와 프로퍼티 접근
class Man{
var age : Int = 0
var weight : Double = 0.0
func display(){
print(age,weight)
}
}
var x : Int
var kim : Man = Man()//클래스명 다음의 괄호는 눈에 보이지 않는 defaultinitializer 나타냄
kim.age = 10
var Kim : Man = Man() //:Man은 생략 가능
var Kim = Man()
class Man{
var age : Int = 0
var weight : Double = 0.0
func display(){
print("나이: \(age), 몸무게: \(weight)")
}
}
var kim : Man = Man()//클래스명 다음의 괄호는 눈에 보이지 않는 defaultinitializer 나타냄
print(kim.age)
kim.age = 10
kim.weight = 20.5
print(kim.age,kim.weight)
kim.display()
<클래스(class or type) 메서드>
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
class func cM(){
print("cM은 클래스 메서드입니다.")
}
static func scM(){
print("scM은 클래스 메서드(static)")
}
}
var han : Man = Man()//클래스명 다음의 괄호는 눈에 보이지 않는 defaultinitializer 나타냄
print(han.age)
han.age = 10
han.weight = 20.5
print(han.age,han.weight)
han.display()//인스턴스 메서드는 인스턴스가 호출
Man.cM()//클래스 메서드는 클래스가 호출/class키워드로 만든 클래스메서드는 자식 클래스에서 override가능 함
Man.scM()//클래스 메서드는 클래스가 호출
*designated initializer
class Man{
var age : Int = 0
var weight : Double = 0.0
init(yourAge: Int, yourWeight : Double){
age = yourAge
weight = yourWeight
} //designated initializer
func display(){
print("나이:\(age), 몸무게:\(weight)")
}
}
var han : Man = Man.init(yourAge: 5, yourWeight:10.3)
print(han.age)
han.display()//인스턴스 메서드는 인스턴스가 호출
han.age = 10
han.weight = 20.5
han.display()
Swift에서 UI 이미지와 관련된 클래스를 사용하는 순서
UIImage: 기본 이미지 클래스로, 이미지 데이터를 저장하고 표시하는 데 사용됩니다.
UIImageView: 이미지를 화면에 표시하는 뷰 클래스입니다. UIImage를 포함하여 화면에 이미지를 보여줍니다.
UIImagePickerController: 사용자에게 사진 라이브러리나 카메라에서 이미지를 선택하게 하는 컨트롤러입니다.
CIImage: Core Image 프레임워크에서 이미지 처리를 위한 클래스입니다. 이미지 필터링 및 변환에 사용됩니다.
PHAsset: Photos 프레임워크에서 사진 및 비디오 자산을 나타내는 클래스입니다. 이미지 및 비디오의 메타데이터에 접근할 수 있습니다.
init 다음에 ?가 있으면 failable initializers
class Man{
var age : Int = 0
var weight : Double = 0.0
init?(age: Int, weight : Double){//? =>failable initializer쓰는 방법
if age <= 0 || weight <= 0.0 {
return nil
}else{
self.age = age
self.weight = weight
}
}
func display(){
print("나이:\(age), 몸무게 :\(weight)")
}
}
var han : Man? = Man(age: 5 ,weight: 10.3)//선언할때 옵셔널 man형으로 선언해야한다 (?,! 붙이기)
han!.display()
han!.age = 10
han!.weight = 20.5
1,2번 많이 씀
class Man{
var age : Int = 0
var weight : Double = 0.0
init?(age: Int, weight : Double){//? =>failable initializer쓰는 방법
if age <= 0 || weight <= 0.0 {
return nil
}else{
self.age = age
self.weight = weight
}
}
func display(){
print("나이:\(age), 몸무게 :\(weight)")
}
}
var han : Man? = Man(age: 0 ,weight: 10.3)
iflet han{// 옵셔널 풀린 han
han.display()
}//좋은 방법
-옵셔널 선언하고~ 풀어주는 방법
class Man{
var age : Int = 0
var weight : Double = 0.0
init?(age: Int, weight : Double){//? =>failable initializer쓰는 방법
if age <= 0 || weight <= 0.0 {
return nil
}else{
self.age = age
self.weight = weight
}
}
func display(){
print("나이:\(age), 몸무게 :\(weight)")
}
}
iflet han = Man(age: 0 ,weight: 10.3){
han.display()
}
class Man {
var age: Int // 나이를 저장할 변수
var weight: Double // 몸무게를 저장할 변수
// 나이와 몸무게를 출력하는 메서드
func display() {
print("나이=\(age), 몸무게=\(weight)")
}
// 초기화 메서드, 나이가 0 이하일 경우 nil 반환
init?(age: Int, weight: Double) {
if age <= 0 {
return nil // 나이가 0 이하이면 nil을 반환하여 인스턴스 생성을 실패시킴
} else {
self.age = age // 나이가 유효하면 초기화
}
self.weight = weight // 몸무게 초기화
}
}
// 1-1. 옵셔널 형으로 선언하여 kim 변수 생성
var kim: Man? = Man(age: 1, weight: 3.5)
// 1-2. 옵셔널 바인딩을 사용하여 kim의 값을 안전하게 언래핑
iflet kim1 = kim {
kim1.display() // kim1이 nil이 아닐 경우 display 메서드 호출
}
// 2. 인스턴스 생성과 동시에 옵셔널 바인딩
iflet kim2 = Man(age: 2, weight: 5.5) {
kim2.display() // kim2가 nil이 아닐 경우 display 메서드 호출
}
// 3. 인스턴스 생성하면서 바로 강제 언래핑
var kim3: Man = Man(age: 3, weight: 7.5)! // Man 인스턴스가 nil이 아님을 확신하고 강제 언래핑
kim3.display() // kim3의 display 메서드 호출
// 4. 옵셔널 인스턴스를 사용시 강제 언래핑
var kim4: Man? = Man(age: 4, weight: 10.5) // 옵셔널 형으로 kim4 생성
kim4!.display() // kim4가 nil이 아님을 확신하고 강제 언래핑하여 display 메서드 호출
상속, 오버라이딩 , failable initializers 이 포함이 된 간단한 소스 예제
// 기본 클래스
class Animal {
var name: String // 동물의 이름
// failable initializer: 이름이 비어있으면 nil 반환
init?(name: String) {
if name.isEmpty {
return nil // 이름이 비어있으면 초기화 실패
}
self.name = name // 이름 초기화
}
// 기본 동물 소리 메서드
func makeSound() {
print("동물이 소리를 냅니다.")
}
}
// 서브 클래스
class Dog: Animal {
// 오버라이드: Dog 클래스의 소리 메서드
override func makeSound() {
print("\(name)는 멍멍하고 짖습니다.") // Dog의 소리
}
}
// 사용 예시
// 1. failable initializer 사용 예
iflet myDog = Dog(name: "Buddy") { // 초기화 성공
myDog.makeSound() // 출력: Buddy는 멍멍하고 짖습니다.
} else {
print("이름이 비어 있습니다.")
}
// 2. failable initializer 실패 예
iflet myCat = Animal(name: "") { // 초기화 실패
myCat.makeSound()
} else {
print("이름이 비어 있습니다.") // 출력: 이름이 비어 있습니다.
}
else절 내의 코드는 현재 코드 흐름을 빠져나갈 수 있는 구문(return, break, continue, throw 등)을 포함해야 하며, 다른 함수를 호출할 수도 있음.
기본 구조
guard<불리언 표현식>else {
// 거짓일 경우 실행될 코드<코드 블록을 빠져 나갈 구문>
}
// 참일 경우 실행되는 코드는 이곳에 위치
조기 출구(early exit): 특정 조건에 맞지 않을 경우 현재의 함수나 반복문에서 빠져나가는 전략을 제공.
Guard~let 활용
사용 범위:return,break,continue,throw등이 가능한 상황에서 사용 가능.
가독성: 옵셔널 바인딩을 통해 다중if~else를 피하고, 코드 가독성을 높임.
함수 외부에서의 사용:guard let으로 언래핑된 변수는guard문 밖에서도 사용할 수 있음. 반면,if문으로 언래핑된 변수는 그 안에서만 사용 가능.
guard~let의 활용
var x = 1
whiletrue {
guard x < 5 else { break }
print(x)
x = x + 1
}
조건식이 거짓일 때:guard문이 실행되어else블록의 코드가 수행되고, 지정된 제어문으로 빠져나감.
조건식이 참일 때: 이후 코드가 정상적으로 실행됨.
guard let~else로 옵셔널 바인딩
func multiplyByTen(value: Int?) {
guard let number = value else {//조건식이 거짓(nil)일 때 else 블록 실행
print("nil")
return
}
print(number*10) //조건식이 참일 때 실행, 주의 : number를 guard문 밖인 여기서도 사용 가능
}
multiplyByTen(value: 3) //30
multiplyByTen(value: nil)//nil
multiplyByTen(value: 10)//100
import Foundation
func sss(x: Int, y: Int) -> (sum: Int, sub: Int, mul: Int, div: Double, mod: Int) {
let sum = x + y
let sub = x - y
let mul = x * y
let div = Double(x) / Double(y) // 같은 자료형만 연산 가능
let mod = x % y // 나머지 연산
return (sum, sub, mul, div, mod)
}
var result = sss(x: 10, y: 3)
print(result.sum) // 13
print(result.sub) // 7
print(result.mul) // 30
print(result.div) // 3.33333333333333
print(result.mod) // 1
// 소수점 3자리로 출력
String(format: "%.3f", result.div)////string은 구조체라서 import Foundation써야함
// 3.333
// 함수의 자료형 출력
print(type(of: sss)) // (Int, Int) -> (sum: Int, sub: Int, mul: Int, div: Double, mod: Int)
가변 매개변수(variadic parameter)
func displayStrings(strings: String...) //string... =>string이 몇개가 와도 관계가 없다
{
for string in strings {
print(string)
}
}
displayStrings(strings: "일", "이", "삼", "사")
displayStrings(strings: "one", "two")
displayStrings(strings: "one", "two","a","b","c")
과제 : 가변 매개변수(variadic parameter) 임의의 개수의 정수 값의 합을 출력하는 함수를 작성하여 호출
func add(numbers:Int...){
var sum:Int=0
for num in numbers{
sum += num
}
print(sum)
}
add(numbers:1,2,3) //6
add(numbers:2,2,2,2,2) //10
add(numbers:1,1,1,1,1,1,1,1,1,1) //10
add(numbers:1,1,1,1) //4
Swift 3에서는 inout의 위치가 바뀜( call by address하고 싶은 매개변수의 자료형 앞에 inout 씀)
var myValue = 10
func doubleValue (value: inout Int) -> Int {
value += value
return(value)
}
print(myValue)//10
print(doubleValue(value : &myValue)) //20
print(myValue)//20
Swift 문자열 서식(swift string format 자리수)
import Foundation
let weight = 60.0
let height = 170.0
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""if bmi >= 40 {
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
print("BMI:\(shortenedBmi), 판정:\(body)")
함수 : 일급 객체 실습
// num에 1을 더하는 함수
func up(num: Int) -> Int {
return num + 1
}
// num에서 1을 빼는 함수
func down(num: Int) -> Int {
return num - 1
}
// up 함수를 toUp 변수에 할당
let toUp = up
// up 함수 호출
print(up(num: 10)) // 결과: 11
// toUp 변수 호출
print(toUp(10)) // 결과: 11
// down 함수를 toDown 변수에 할당
let toDown = down
// 함수 타입을 매개변수로 받아 결과를 출력하는 함수
func upDown(Fun: (Int) -> Int, value: Int) {
let result = Fun(value)
print("결과 = \(result)")
}
// toUp(10) 호출
upDown(Fun: toUp, value: 10) // 결과: 결과 = 11
// toDown(10) 호출
upDown(Fun: toDown, value: 10) // 결과: 결과 = 9
// Bool 값을 기반으로 함수를 결정하여 반환하는 함수
func decideFun(x: Bool) -> (Int) -> Int {
if x {
return toUp // true일 경우 toUp 반환
} else {
return toDown // false일 경우 toDown 반환
}
}
// decideFun 호출하여 true일 경우 toUp을 반환
let r = decideFun(x: true) // let r = toUp
print(type(of: r)) // 결과: (Int) -> Int
// r(10) 호출
print(r(10)) // 결과: 11 (toUp(10) 호출)
first class object : (1)함수를 변수에 저장 가능
// 인치를 피트로 변환하는 함수
func inchesToFeet(inches: Float) -> Float {
return inches * 0.0833333 // 1인치는 약 0.0833 피트
}
// inchesToFeet 함수를 toFeet 변수에 할당하여 함수처럼 사용
let toFeet = inchesToFeet
// toFeet 함수를 사용하여 인치 값을 피트로 변환
let result = toFeet(10) // 10 인치를 피트로 변환
print(result) // 결과 출력
클로저 표현식
// 두 정수를 더하는 함수
func add(x: Int, y: Int) -> Int {
return (x + y)
}
print(add(x: 10, y: 20)) // 결과: 30
print(type(of:add))
// 클로저 정의
let add1 = { (x: Int, y: Int) -> Int inreturn (x + y) // 중괄호가 필요
}
print(add1(10, 20)) // 결과: 30
print(type(of:add1))
func add(_ x: Int, with y: Int) -> Int {
print(#function)//add(:with:)return x + y
}
print(add(10, with: 20))//30
func add1(x: Int, y: Int) -> Int {
print(#function)//add1(x:y:)return x + y
}
print(add1(x: 10, y: 20))//30
func add2(first x: Int, second y: Int) -> Int {
print(#function)//add2(first:second:)return x + y
}
print(add2(first: 10, second: 20))//30
func add3(_ x: Int, _ y: Int) -> Int {
print(#function)//add3(_:_:)return x + y
}
print(add3(10,20))//30
// 1. multiplyByTen 함수
func multiplyByTen(value: Int?) {
guard let number = value else {
print("nil")
return
}
print(number * 10)
}
multiplyByTen(value: 3) // 30
multiplyByTen(value: nil) // nil
multiplyByTen(value: 10) // 100
// 2. printName 함수
func printName(firstName: String, lastName: String?) {
// ifletiflet lName = lastName {
print(lName, firstName)
} else {
print("성이 없네요!")
}
// guard let
guard let lName = lastName else {
print("성이 없네요!")
return
}
print(lName, firstName)
}
printName(firstName: "길동", lastName: "홍") // 홍 길동
printName(firstName: "길동", lastName: nil) // 성이 없네요!
// 3. sayHello 함수
func sayHello(count: Int, name: String = "길동") -> String {
return ("\(name), 너의 번호는 \(count)")
}
print(sayHello(count: 1)) // 길동, 너의 번호는 1
print(sayHello(count: 2, name: "철수")) // 철수, 너의 번호는 2
// 4. converter 함수
func converter(length: Float) -> (yards: Float, centimeters: Float, meters: Float) {
let yards = length * 0.0277778
let centimeters = length * 2.54
let meters = length * 0.0254
return (yards, centimeters, meters)
}
var lengthTuple = converter(length: 10)
print(lengthTuple) // 길이를 변환한 튜플 출력
print(lengthTuple.yards) // 0.277778
print(lengthTuple.centimeters) // 25.4
print(lengthTuple.meters) // 0.254
// 5. sss 함수
func sss(x: Int, y: Int) -> (sum: Int, sub: Int, div: Double) {
let sum = x + y
let sub = x - y
let div = Double(x) / Double(y) // 같은 자료형만 연산 가능
return (sum, sub, div)
}
var result = sss(x: 10, y: 3)
print(result.sum) // 13 // 합계 출력
print(result.sub) // 7 // 차이 출력
print(result.div) // 3.3333333333333335 // 나눗셈 결과 출력
// 6. displayStrings 함수
func displayStrings(strings: String...) {
for string in strings {
print(string)
}
}
displayStrings(strings: "일", "이", "삼", "사") // 출력값: 일 이 삼 사
displayStrings(strings: "one", "two") // 출력값: one two
/////////
var myValue = 10
func doubleValue(value: inout Int) -> Int {
value += value
return(value)
}
print(myValue) // 출력값: 10
print(doubleValue(value: &myValue)) // 출력값: 20
print(myValue) // 출력값: 20
////////
let weight = 60.0
let height = 170.0
let bmi = weight / (height * height * 0.0001) // kg/m*m
var body = ""if bmi >= 40 {
body = "3단계 비만"
} elseif bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} elseif bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} elseif bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
print("BMI:\(bmi), 판정:\(body)") // 출력값: BMI:20.761245674740486, 판정: 정상
// 9. calcBMI 함수
import Foundation
func calcBMI(weight: Double, height: Double) { // Void형
let bmi = weight / (height * height * 0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
switch bmi {
case 0.0..<18.5:
print("BMI:\(shortenedBmi),판정:저체중")
case 18.5..<25.0:
print("BMI:\(shortenedBmi),판정:정상")
case 25.0..<30.0:
print("BMI:\(shortenedBmi),판정:1단계 비만")
case 30.0..<40.0:
print("BMI:\(shortenedBmi),판정:2단계 비만")
default:
print("BMI:\(shortenedBmi),판정:3단계 비만")
}
}
calcBMI(weight: 62.5, height: 172.3) // BMI:21.0, 판정:정상 // BMI 계산 및 판정 출력