Video Scene의 Custom Class를 지정해줘야해요
VideoViewController를 지정할 것이라면 V만 쳐도 자동으로 나타나야 하는데
저는 나타나질 않습니다 ㅠㅠ
commed + R 로 한번 실행하고 되니깐 갑자기 됩니다!
iOS 함수중에 중요한 함수에요.
# present 함수
iOS에서 present 함수는 새로운 뷰 컨트롤러를 화면에 표시하는 데 사용되는 메서드입니다. 보통 새로운 화면을 띄울 때 사용하며, 특히 **모달 방식(modal)**으로 뷰 컨트롤러를 표시할 때 유용합니다. 모달 방식은 새로운 화면이 기존 화면 위에 덮어져서 나타나는 형태를 말합니다.
이 메서드는 다음과 같은 세 가지 파라미터를 가집니다:
- viewControllerToPresent (UIViewController)
- 이 파라미터는 현재 화면에 표시될 새로운 뷰 컨트롤러입니다.
- 예를 들어, AVPlayerViewController, UIAlertController, 혹은 사용자 정의 뷰 컨트롤러가 될 수 있습니다.
- 이 뷰 컨트롤러는 현재 화면 위에 "모달" 방식으로 표시됩니다. 모달 방식은 새로운 화면이 기존 화면 위에 나타나는 방식으로, 사용자가 새로운 화면을 닫을 때까지 원래 화면으로 돌아갈 수 없습니다.
- animated (Bool)
- Bool 타입으로, 화면 전환이 애니메이션을 통해 이루어질지 여부를 결정합니다.
- true: 화면 전환이 애니메이션을 통해 이루어집니다.
- false: 화면 전환이 즉시 이루어지며 애니메이션 없이 새로운 뷰가 나타납니다.
- 일반적으로 화면 전환 시 애니메이션을 사용하는 것이 자연스러워 true로 설정합니다.
- Bool 타입으로, 화면 전환이 애니메이션을 통해 이루어질지 여부를 결정합니다.
- completion ((() -> Void)?)
- completion은 화면 전환이 완료된 후 실행될 클로저입니다.
- 이 파라미터는 옵셔널이며 기본값은 nil입니다. 즉, 특별히 처리할 일이 없다면 생략할 수 있습니다.
- 예를 들어, 화면 전환 후 비디오를 재생하거나, 어떤 처리를 완료했음을 알리는 작업을 할 수 있습니다.
present의 동작 방식
- 모달 방식: present 메서드는 새로운 뷰 컨트롤러를 현재 화면 위에 모달 방식으로 표시합니다. 즉, 새로운 뷰가 기존 화면을 덮어씁니다. 사용자가 모달 화면을 닫을 때까지 원래 화면으로 돌아갈 수 없습니다.
- 뒤로 가기: 모달 방식으로 띄운 화면은 dismiss 메서드를 호출하여 닫을 수 있습니다. 보통 화면을 닫을 때는 dismiss(animated: true)를 사용하여 애니메이션을 추가하여 원래 화면으로 돌아갑니다.
동영상을 재생시킬 수 있는 코드입니다. 하지만 완벽한 코드는 아니에요
import UIKit
import AVKit
class VideoViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func playVideo(_ sender: UIButton) {
let videoPath : String? = Bundle.main.path(forResource: "bmi", ofType: "mp4")
let videoURL = URL(filePath: videoPath!)
let player = AVPlayer(url: videoURL)
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true)
player.play(
1. URL(filePath:) 사용 오류
- URL(filePath:)는 파일 경로를 나타내는 문자열을 사용하여 URL을 생성하는 초기화 방식입니다. 하지만 Bundle.main.path(forResource:ofType:) 메서드는 String?을 반환하므로, 이를 URL(filePath:)에 그대로 전달하는 것은 안전하지 않습니다.
- URL(filePath:) 대신 URL(fileURLWithPath:)를 사용하는 것이 더 안전합니다.
2. 강제 언래핑 (!)
- videoPath! 부분에서 강제로 언래핑을 하고 있습니다. 이는 만약 파일이 존재하지 않으면 앱이 크래시를 일으킬 수 있습니다. 이를 안전하게 처리하려면 옵셔널 바인딩(if let 또는 guard let)을 사용하여 안전하게 값을 처리해야 합니다.
3. present 후 player.play() 호출 순서
- present 메서드는 비동기적으로 작동하므로, player.play()가 present 호출 이후에 실행되어야 합니다. present가 완료된 후에 비디오가 재생되도록 하기 위해서는 completion 블록을 사용해야 합니다.
수정본
import UIKit
import AVKit
import AVFoundation
class VideoViewController: UIViewController {
@IBAction func playVideo(_ sender: UIButton) {
// 비디오 파일 경로 가져오기 (Bundle에서)
if let videoPath = Bundle.main.path(forResource: "bmi", ofType: "mp4") {
let videoURL = URL(fileURLWithPath: videoPath) // 올바른 URL 생성
// AVPlayer 인스턴스 생성
let player = AVPlayer(url: videoURL)
// AVPlayerViewController 인스턴스 생성
let playerController = AVPlayerViewController()
playerController.player = player
// 모달로 AVPlayerViewController 표시
present(playerController, animated: true, completion: {
// 플레이어가 화면에 나타난 후 비디오 재생
player.play()
})
} else {
print("비디오 파일을 찾을 수 없습니다.")
}
}
}
# 첫번째 영상 재생 코드에 대한 자세한 설명
import UIKit
import AVKit
import AVFoundation
class VideoViewController: UIViewController {
// 비디오를 재생할 때 호출되는 액션 메서드
@IBAction func playVideo(_ sender: UIButton) {
// 1. Bundle에서 비디오 파일 경로를 안전하게 가져옵니다.
if let videoPath = Bundle.main.path(forResource: "bmi", ofType: "mp4") {
// 2. videoPath가 유효한 경로일 경우, 이를 사용해 URL을 생성합니다.
// Bundle에서 가져온 경로를 통해 fileURLWithPath()로 URL을 생성
let videoURL = URL(fileURLWithPath: videoPath)
// 3. AVPlayer 객체를 생성하여 비디오 파일을 로드합니다.
// AVPlayer는 AVPlayerViewController에서 재생할 수 있는 미디어를 처리하는 객체입니다.
let player = AVPlayer(url: videoURL)
// 4. AVPlayerViewController 객체를 생성합니다.
// AVPlayerViewController는 비디오 재생 화면을 제공하는 컨트롤러로, 플레이어를 연결할 수 있습니다.
let playerController = AVPlayerViewController()
playerController.player = player // AVPlayer를 AVPlayerViewController에 연결
// 5. AVPlayerViewController를 화면에 모달로 표시합니다.
// present() 메서드는 현재 뷰 컨트롤러에서 다른 뷰 컨트롤러를 표시할 때 사용됩니다.
// 모달로 표시되며, animated: true로 애니메이션을 추가하고,
// completion 블록에서 비디오 재생을 시작합니다.
present(playerController, animated: true, completion: {
// 화면 전환 후 AVPlayer가 준비되면 비디오를 재생 시작합니다.
// player.play()는 AVPlayer 객체에서 비디오 재생을 시작하는 메서드입니다.
player.play()
})
} else {
// 6. 만약 비디오 파일이 존재하지 않는 경우, 콘솔에 오류 메시지를 출력합니다.
// Bundle에서 해당 파일을 찾을 수 없는 경우를 처리합니다.
print("비디오 파일을 찾을 수 없습니다.")
}
}
}
# guard let 을 이용한 방법
@IBAction func playVideo(_ sender: UIButton) {
guard let videoPath = Bundle.main.path(forResource: "bmi", ofType: "mp4") else{ return }
let videoURL = URL(filePath: videoPath)
let player = AVPlayer(url: videoURL)
let playerController = AVPlayerViewController()
playerController.player = player
present(playerController, animated: true)
player.play()
}
1. 명확한 오류 처리 및 흐름 제어
- guard let은 주로 필수 조건을 확인하고, 그 조건을 만족하지 않으면 즉시 메서드를 종료하도록 하는 데 사용됩니다.
- guard를 사용하면 **"if 조건을 만족하지 않으면 즉시 종료"**라는 의도가 명확하게 표현됩니다.
- 반면, if let은 조건을 만족하는 경우에만 이후 코드가 실행되기 때문에, 조건을 만족하지 않았을 때의 흐름이 덜 명확합니다.
guard let의 특징:
- 조건이 충족되지 않으면 메서드나 함수에서 **즉시 return**하거나 throw 등으로 흐름을 종료시킵니다.
- 조건이 성공했을 때만 이후 코드를 계속 실행합니다.
- 코드 흐름이 명확하고 깔끔해집니다. 조건을 체크하고, 조건을 만족하지 않으면 바로 return하기 때문에, 그 후의 코드가 조건을 만족하는 경우에만 실행될 것이라는 보장이 생깁니다.
if let의 특징:
- 조건이 만족되면 실행되지만, 만족하지 않을 때 추가 코드가 실행됩니다. if 블록 안에서 추가 작업을 해야 하므로, 흐름이 좀 더 복잡해집니다.
- if let을 사용할 때 조건이 만족하지 않으면 나머지 코드를 계속 진행할 수 있기 때문에, 코드 흐름을 더 어렵게 만들 수 있습니다.
2. 코드 가독성 및 유지보수성
- guard let은 조건이 만족하지 않으면 메서드의 실행을 중단시키므로, 메서드 바깥에서 어떤 작업을 해야 하는지 명확하게 보여줍니다.
- 이를 통해 코드의 가독성과 유지보수성이 높아집니다. 특히, 조건이 중요한 경우(예: 비디오 파일 경로가 없을 때), 그 처리를 guard로 명확하게 보여주면 코드를 빠르게 이해할 수 있습니다.
여기서 Enter를 치면 { code } 로 바뀌어요
이거는 url만 있으면 보여주는 내장 Web view에요
강제 언래핑을 하여 사용 할수 있지만 권장하는 방법은 아니기때문에 guard let을 이용해보도록 해요
중복되는 부분을 제거하여 리펙토링 했습니다
@ WebViewController를 문서화
import UIKit
import WebKit
/**
`WebViewController`는 웹 페이지를 로드하고 표시하는 뷰 컨트롤러입니다.
이 클래스는 초기 로드 시 기본 웹사이트를 로드하고, 버튼을 클릭하여 다른 웹사이트로 이동하는 기능을 제공합니다.
*/
class WebViewController: UIViewController {
// MARK: - IBOutlet
/// 웹 페이지를 표시하는 `WKWebView` 객체
/// 이 IBOutlet을 통해 스토리보드에서 연결된 웹 뷰를 사용할 수 있습니다.
@IBOutlet weak var webView: WKWebView!
// MARK: - Lifecycle Methods
/**
뷰가 로드될 때 호출되는 메서드입니다.
기본적으로 `viewDidLoad()`에서 설정한 URL (https://m.youtube.com)로 첫 번째 웹 페이지를 로드합니다.
*/
override func viewDidLoad() {
super.viewDidLoad()
// 기본 웹사이트 로드 (Tistory)
loadWebsite(urlString: "https://sw0913.tistory.com/")
}
// MARK: - Actions
/**
'Naver' 버튼이 클릭되었을 때 호출되는 메서드입니다.
클릭 시, 네이버 모바일 웹사이트 (`m.naver.com`)를 로드합니다.
- Parameter sender: 버튼 클릭 이벤트
*/
@IBAction func goNaver(_ sender: UIButton) {
// 네이버 웹사이트 로드
loadWebsite(urlString: "https://m.naver.com")
}
// MARK: - Helper Methods
/**
주어진 URL 문자열을 사용하여 웹 페이지를 로드하는 메서드입니다.
`URL`이 유효한 경우, 해당 URL을 `WKWebView`에서 로드합니다.
- Parameter urlString: 로드할 웹 페이지의 URL 문자열
*/
private func loadWebsite(urlString: String) {
// URL 객체를 생성하고 유효성 검사
guard let url = URL(string: urlString) else {
print("Invalid URL: \(urlString)") // URL이 유효하지 않으면 콘솔에 오류 메시지 출력
return
}
// URLRequest 객체 생성
let request = URLRequest(url: url)
// 웹 뷰에서 URL 요청 로드
webView.load(request)
}
}
문서화된 코드 설명:
1. 클래스 설명 (WebViewController):
- WebViewController는 UIViewController를 상속받는 뷰 컨트롤러 클래스입니다. 이 클래스는 WKWebView를 사용하여 웹 페이지를 로드하고 표시합니다. 뷰가 로드될 때 기본 URL을 표시하고, 버튼 클릭 시 다른 웹사이트로 이동하는 기능을 제공합니다.
2. 변수 설명:
- @IBOutlet weak var webView: WKWebView!:
- WKWebView 객체는 웹 페이지를 표시하는 데 사용됩니다. 이 객체는 스토리보드에서 연결되어 있으며, 뷰가 로드되면 지정된 URL을 표시합니다.
- IBOutlet을 사용하여 스토리보드와 연결된 UI 요소를 코드에서 접근할 수 있게 합니다.
3. 메서드 설명:
- viewDidLoad():
- viewDidLoad()는 뷰가 메모리에 로드된 후 한 번만 호출되는 메서드입니다. 이 메서드 내에서 기본 웹사이트를 로드하는 작업을 합니다.
- loadWebsite(urlString:) 메서드를 호출하여 기본 웹사이트 (https://sw0913.tistory.com)를 웹 뷰에 로드합니다.
- goNaver(_ sender: UIButton):
- 네이버 버튼을 클릭했을 때 호출되는 액션 메서드입니다.
- 네이버 모바일 웹사이트 (https://m.naver.com)를 로드하는 기능을 수행합니다.
- loadWebsite(urlString:) 메서드를 호출하여 네이버 사이트를 웹 뷰에 로드합니다.
- loadWebsite(urlString:):
- urlString을 매개변수로 받아, 해당 URL을 웹 뷰에 로드하는 기능을 수행하는 헬퍼 메서드입니다.
- URL 문자열이 유효한지 확인한 후, 유효한 URL이면 URLRequest를 생성하고 이를 WKWebView에 로드합니다.
- 만약 URL이 잘못되었을 경우, guard let으로 처리하여 안전하게 오류를 처리하고, 유효하지 않은 URL에 대해서는 콘솔에 오류 메시지를 출력합니다.
4. MARK:
- // MARK: 주석을 사용하여 코드의 섹션을 구분하고, 가독성을 높였습니다. 각 섹션은 다음과 같습니다:
- IBOutlet: 인터페이스 요소와 연결된 변수들.
- Lifecycle Methods: 뷰 컨트롤러의 생명 주기와 관련된 메서드들 (예: viewDidLoad).
- Actions: 사용자 인터페이스의 이벤트(버튼 클릭 등)에 반응하는 메서드들.
- Helper Methods: 재사용 가능한 보조 메서드들 (예: loadWebsite).
@ ViewController를 문서화
import UIKit
/**
`ViewController` 클래스는 BMI(체질량지수)를 계산하는 뷰 컨트롤러입니다.
사용자로부터 키와 체중을 입력받아 BMI를 계산하고, 결과를 화면에 표시합니다.
*/
class ViewController: UIViewController {
// MARK: - IBOutlet
/// 사용자로부터 입력받은 키 텍스트 필드
@IBOutlet weak var txtHeight: UITextField!
/// 사용자로부터 입력받은 체중 텍스트 필드
@IBOutlet weak var txtWeight: UITextField!
/// 계산된 BMI 결과를 표시할 라벨
@IBOutlet weak var lblResult: UILabel!
// MARK: - Actions
/**
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)
// 소수점 1자리까지 표시
let shortenedBmi = String(format: "%.1f", bmi)
// BMI에 따라 체중 상태를 판단하고 색상 및 메시지 설정
var body = ""
var color = UIColor.white
if bmi >= 40 {
// 3단계 비만 (BMI 40 이상)
color = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
body = "3단계 비만"
} else if bmi >= 30 && bmi < 40 {
// 2단계 비만 (BMI 30 이상 40 미만)
color = UIColor(displayP3Red: 0.7, green: 0.0, blue: 0.0, alpha: 1.0)
body = "2단계 비만"
} else if bmi >= 25 && bmi < 30 {
// 1단계 비만 (BMI 25 이상 30 미만)
color = UIColor(displayP3Red: 0.4, green: 0.0, blue: 0.0, alpha: 1.0)
body = "1단계 비만"
} else if bmi >= 18.5 && bmi < 25 {
// 정상 체중 (BMI 18.5 이상 25 미만)
color = UIColor(displayP3Red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
body = "정상"
} else {
// 저체중 (BMI 18.5 미만)
color = UIColor(displayP3Red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0)
body = "저체중"
}
// 계산된 결과를 라벨에 표시
lblResult.backgroundColor = color // BMI 범위에 맞는 배경색 설정
lblResult.clipsToBounds = true // 라벨의 배경색이 라벨 모서리를 넘어가지 않도록 설정
lblResult.text = "BMI:\(shortenedBmi), 판정:\(body)" // BMI 값과 체중 상태를 표시
lblResult.layer.cornerRadius = 10 // 라벨의 모서리를 둥글게 설정
}
}
// MARK: - Lifecycle Methods
/**
뷰가 로드될 때 호출되는 메서드입니다.
추가적인 초기 설정이 필요할 경우 여기에 작성합니다.
*/
override func viewDidLoad() {
super.viewDidLoad()
// 추가적인 설정이 필요하면 여기에 작성
}
}
문서화된 코드 설명:
1. 클래스 설명 (ViewController):
- ViewController는 사용자가 입력한 키와 체중을 바탕으로 BMI를 계산하고 그 결과를 화면에 표시하는 뷰 컨트롤러입니다. 계산된 BMI 값에 따라 적절한 체중 상태(저체중, 정상, 1단계 비만, 2단계 비만, 3단계 비만)를 출력합니다.
- UILabel을 사용하여 계산 결과를 화면에 표시하고, BMI 값에 따라 배경색과 텍스트 색상을 다르게 설정하여 시각적으로 명확한 피드백을 제공합니다.
2. 변수 설명:
- @IBOutlet weak var txtHeight: UITextField!:
- 사용자가 키를 입력하는 텍스트 필드입니다.
- @IBOutlet weak var txtWeight: UITextField!:
- 사용자가 체중을 입력하는 텍스트 필드입니다.
- @IBOutlet weak var lblResult: UILabel!:
- BMI 계산 결과와 체중 상태를 표시하는 레이블입니다. 이 레이블의 배경색과 텍스트는 BMI 계산 결과에 따라 동적으로 변경됩니다.
3. 메서드 설명:
- calcBmi(_ sender: UIButton):
- 사용자가 BMI 계산 버튼을 클릭하면 호출됩니다.
- txtHeight와 txtWeight에서 사용자가 입력한 값을 가져와 BMI를 계산하고, 결과를 lblResult에 표시합니다.
- 만약 입력값이 비어 있으면 경고 메시지를 표시하고 계산을 중단합니다.
- BMI 값에 따라 적절한 체중 상태를 판별하고, 해당 상태에 맞는 텍스트와 배경색을 설정합니다.
- bmi 계산식은 weight / (height * height * 0.0001)입니다. 여기서 0.0001은 height가 cm 단위일 때 m 단위로 변환하기 위한 계수입니다.
- viewDidLoad():
- 뷰가 로드된 후 호출되는 메서드입니다. 여기서는 특별한 설정이 필요하지 않지만, 나중에 뷰가 로드될 때 초기화 작업이 필요하다면 여기에 작성할 수 있습니다.
4. 동작 설명:
- BMI 계산:
- BMI는 체중(kg)을 키(m)의 제곱으로 나눈 값으로 계산됩니다.
- BMI 공식: BMI = weight / (height * height) (단, height는 m 단위여야 하므로, cm 단위로 입력받은 경우 height * height * 0.0001로 계산합니다.)
- 체중 상태 분류:
- BMI 값에 따라 체중 상태를 다섯 가지 범주로 분류합니다:
- 3단계 비만: BMI ≥ 40
- 2단계 비만: 30 ≤ BMI < 40
- 1단계 비만: 25 ≤ BMI < 30
- 정상 체중: 18.5 ≤ BMI < 25
- 저체중: BMI < 18.5
- BMI 값에 따라 체중 상태를 다섯 가지 범주로 분류합니다:
- 결과 표시:
- lblResult에 BMI 값과 해당 체중 상태를 출력합니다. 또한, BMI 값에 따라 배경색과 텍스트 색상이 변경되며, 라벨은 둥근 모서리를 갖도록 설정됩니다.
5. 배경색 설정:
- 각 체중 상태에 맞게 배경색을 다르게 설정합니다:
- 3단계 비만: 빨간색
- 2단계 비만: 어두운 빨간색
- 1단계 비만: 어두운 붉은색
- 정상: 파란색
- 저체중: 초록색
출처: smile Han https://www.youtube.com/channel/UCM8wseo6DkA-D7yGlCrcrwA