label 복사할 땐 코드와 연결되어있지 않은 상태에서 연결할 것

키보드 타입을 바꿔줘서 숫자만 입력할 수 있도록 유도한다.

중복 연결이 된 것은 없는지 잘 확인한다.
*** 문자열 123을 Intager형으로 바꿀 때 ***
Int("123")으로 바꿔준다.
이 때 123은 옵셔널 변수 형태로 나오게 되는데,

실패 가능한 initializer
뤼튼 대답
문자열 "123"을 정수로 바꿀 때 옵셔널(Optional) 타입이 되는 이유는, 변환이 항상 100% 성공한다는 보장이 없기 때문이야.
즉, "123"처럼 숫자로 변환 가능한 문자열도 있지만, "abc" 같은 숫자가 아닌 문자열을 정수로 바꾸려고 하면 실패하잖아? Swift의 Int 생성자 init?(_:)는 이런 상황을 대비해서 숫자로 변환이 불가능할 때는 nil을 반환하도록 옵셔널로 설계되어 있어.
그래서 Int("123")은 옵셔널 정수(Int?)형으로 결과가 나오고, 값이 올바른 경우에는 내부에 123이라는 값이 들어있지만, 변환 불가능하면 nil이 들어있게 되는 거야.
이런 구조 덕에 변환 실패 시 오류가 나지 않고, 안전하게 실패 여부를 검사할 수 있어서 코드가 더 안정적이고 깔끔해짐.


탭 바를 붙이면 기본적으로 item scene이 된다. 몇개를 만들어도 이름이 똑같다. 그래서 만들자마자 이름을 바꿔주어야한다.
segue : 세그웨이


새 화면을 만들고 그 화면의 동작을 수행할 swift 클래스 파일을 만들었을 때 연결까지 잘 시켜주어야한다.
연결시킬 때는 웬만하면 자동완성시켜서 연결한다.
videoViewController 소스 주석 달아서 뤼튼이 답변해줌.
import UIKit // iOS 앱 개발의 기본 프레임워크! UI(버튼, 화면 등) 만들 때 필수.
import AVKit // 동영상 재생 컨트롤러(AVPlayerViewController) 제공하는 프레임워크!
import AVFoundation // 오디오/비디오 미디어 데이터 다룰 때 쓰는 코어 프레임워크!
class VideoViewController: UIViewController { // 'VideoViewController'라는 화면 관리 클래스 시작! 'UIViewController' 기본 상속.
@IBAction func playVideo(_ sender: Any) { // @IBAction: 스토리보드의 UI (예: 버튼)랑 연결될 함수! 버튼 누르면 이 코드가 실행됨.
// 번들에서 bmi.mp4 경로 찾기 // 주석: 앱 안에 있는 'bmi.mp4' 파일의 위치를 찾는 부분이야.
guard let filePath = Bundle.main.path(forResource: "bmi", ofType: "mp4") else { // guard let: 'filePath' 변수에 파일 경로를 안전하게 할당해보고, 실패하면 else 블록 실행!
// Bundle.main: 현재 실행 중인 앱의 메인 번들을 의미.
// path(forResource: "bmi", ofType: "mp4"): "bmi"라는 이름의 "mp4" 파일을 찾아서 그 경로를 문자열로 가져옴.
print("bmi.mp4 파일 없어!") // else 블록: 만약 'bmi.mp4' 파일을 못 찾으면 이 메시지 출력!
return // else 블록: 파일을 못 찾았으니 더 이상 진행하지 않고 함수 종료!
}
let fileURL = URL(fileURLWithPath: filePath) // filePath (문자열)을 동영상이 재생될 수 있는 URL 형태로 변환! 'AVPlayer'가 URL을 받거든.
// AVPlayer + AVPlayerViewController 준비 // 주석: 동영상 재생을 위한 플레이어와 UI를 만드는 부분!
let player = AVPlayer(url: fileURL) // AVPlayer: 실제 동영상을 재생하는 엔진! 위에서 만든 fileURL을 사용해서 초기화.
let playerVC = AVPlayerViewController() // AVPlayerViewController: 동영상 재생 UI(재생/정지 버튼, 시간 표시 등)를 가진 뷰 컨트롤러!
playerVC.player = player // 플레이어 뷰 컨트롤러(UI)에 위에서 만든 AVPlayer(재생 엔진)를 연결!
// 뷰 컨트롤러 계층에 플레이어 뷰 추가 // 주석: 동영상 재생 화면을 현재 앱 화면에 띄우는 복잡하지만 중요한 단계!
self.addChild(playerVC) // 현재 'VideoViewController'에 'playerVC'를 자식 뷰 컨트롤러로 추가. 필수야!
playerVC.view.frame = self.view.bounds // 'playerVC'의 화면 크기를 현재 뷰 컨트롤러의 전체 크기(`self.view.bounds`)로 맞춤.
self.view.addSubview(playerVC.view) // 'playerVC'의 화면을 현재 뷰 컨트롤러의 화면 위에 올림! 이제 눈에 보이게 됨.
playerVC.didMove(toParent: self) // 'playerVC'가 부모 뷰 컨트롤러('self')에 완전히 추가되었다고 알려주는 함수 호출. 이거 해줘야 제대로 작동함!
// 자동 재생 시작 // 주석: 동영상 바로 재생 고고!
player.play() // AVPlayer에게 동영상 재생 시작 명령!
}
override func viewDidLoad() { // viewDidLoad(): 이 뷰 컨트롤러의 화면이 메모리에 로드된 직후에 딱 한 번 실행되는 함수! 초기 설정할 때 많이 씀.
super.viewDidLoad() // 부모 클래스의 viewDidLoad()도 실행해줘야 함!
// Do any additional setup after loading the view. // 주석: 여기에 필요한 초기 설정 코드를 추가할 수 있어.
}
}

프레임워크를 import 시키지 않았을 때 나타나는 오류메시지