한 스토리보드 내에 여러 viewController를 가질 수 있는데 이때 각각의 viewController를 identify할 수 있는 기준은 인스펙터창의 Identity - Storyboard ID값이다.
//let storyBoard = UIStoryboard(name: "Main", bundle: nil)
// self를 작성해주면 자기 자신이 가지고 있는 스토리보드가 뭔지 바로 알 수 있음!
let vc = self.storyboard?.instantiateViewController(withIdentifier: "BoxOfficeTableViewController") as! BoxOfficeTableViewController
// 3. Push
// navigation controller가 Embed 돼있는 경우에만 작동~
// 즉 스토리보드에서의 구조와 코드에서 작동하는 구조가 동일해야함~
self.navigationController?.pushViewController(vc, animated: true)
// 1. 스토리보드 특정
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
// 2. 어떤 뷰 컨트롤러로 전환을 해줄지, 스토리보드 내 많은 뷰 컨트롤러 중, 전환하고자 하는 뷰 컨트롤러 가져오기
// 스토리보드랑 클래스 파일을 매칭한거!
// 이때 vc의 타입은 UIViewController
let vc = storyBoard.instantiateViewController(withIdentifier: "MemoTableViewController") as! MemoTableViewController
// 2-1. 네비게이션 컨트롤러 임베드 시키기!
let nav = UINavigationController(rootViewController: vc)
// (옵션) 스타일 설정이 가능함
nav.modalPresentationStyle = .fullScreen // 탭바는 살아있는 풀스크린
nav.modalTransitionStyle = .flipHorizontal
// 3. 어떻게 화면 전화할지 선택하면 됨! (present)
self.present(nav, animated: true, completion: nil)
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(closeButtonClicked))
@objc func closeButtonClicked() {
self.dismiss(animated: true, completion: nil)
}
// 이 경우에는 present로 화면을 띄웠기 때문에 dismiss로 화면을 없애줘야한다.
만약 첫 화면으로 띄우고싶은 스토리보드 파일을 변경하고 싶다면
1. Main Interface에서 파일 이름을 변경해준다.
2. Info.plist에서 Storyboard Name까지 변경해주면 시작하는 파일을 변경할 수 있다.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .plain, target: self, action: #selector(closeButtonClicked))
}
@objc func closeButtonClicked() {
self.dismiss(animated: true, completion: nil)
}
selector
란 메서드를 식별할 수 있는 고유한 이름.let selector = #selector(selectorName)
@objc
를 붙여야한다.class detailBoxOfficeViewController: UIViewController, UITextViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource
위의 코드는 viewController class를 정의하는 코드다. 이 중, UIViewController는 상속한 class
, 나머지 UITextViewDelegate 등은 모두 프로토콜
이다! (몰랐음)
프로토콜의 기본 형태
protocol 프로토콜이름 {
프로토콜 내용
}
protocol Introduce {
var name: String { get set }
var age: Int { get }
func introduct()
}
class Human {}
class Jack: Human, Introduce {
var name: String = "jack"
var age: Int = 25
func introduce() {
print("자기소개하기")
}
}
UITableViewDataSource
, UITableViewDelegate
모두 프로토콜이다. 이를 채택했을 때 반드시 정의해야하는 numberOfRowsInSection
, cellForRowAt
메서드 모두 프로토콜 내부에 정의되어있다.UITableViewDataSource
내부를 들여다보면 다음과 같이 코드가 구현돼있다. optional
이 붙어있지 않은 가장 위의 두 메서드가 항상 우리가 프로토콜을 채택했을 때 구현해야하는 메서드임을 볼 수 있다. 내부 코드를 통해 내용은 정의되어있지 않음을 확인할 수 있다.public protocol UITableViewDataSource : NSObjectProtocol {
@available(iOS 2.0, *)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
@available(iOS 2.0, *)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
@available(iOS 2.0, *)
optional func numberOfSections(in tableView: UITableView) -> Int // Default is 1 if not implemented
@available(iOS 2.0, *)
optional func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? // fixed font style. use custom view (UILabel) if you want something different
UIPickerView
다. @IBOutlet weak var titleTextField: UITextField!
override func viewDidLoad() {
// IBOutlet으로 연결된 textField를 클릭했을 때 pickerView가 뜨도록 설정하는 코드
let pickerView = UIPickerView()
pickerView.dataSource = self
pickerView.delegate = self
titleTextField.inputView = pickerView
}
let pickerList: [String] = ["감자", "고구마", "파인애플", "자두", "복숭아"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
// 총 돌아가는 Wheel, 구역을 몇 개로 설정할 것 인지
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
// 몇 개의 선택지를 제공할건지
return 5
}
// 위의 두 메서드가 pickerView 프로토콜의 필수 메서드
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
// 각 row별 title 설정하는 메서드
return "\(pickerList[row])"
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// 각 row별 선택했을 때 사용되는 메서드
print("\(row)")
}
@IBOutlet weak var overviewTextView: UITextView!
override func viewDidLoad() {
overviewTextView.delegate = self
// 초기에 어떤 모양으로 세팅해줄건지
// 내용, 색상 설정
overviewTextView.text = "overviewTextView"
overviewTextView.textColor = .lightGray
}
// 커서 시작
func textViewDidBeginEditing(_ textView: UITextView) {
// 아직 글자가 써지지 않은 상태, place holder가 있는 상태를 글자색으로 구분
// 여기서 textView로 설정하면 뷰컨트롤러 내의 delegate가 연결된 모든 textView에 설정이 적용된다.
// 만약 특정 textView에만 설정하고 싶은 경우에는 IBOutlet 이름으로 설정해주면 됨
if textView.textColor == .lightGray {
textView.text = nil
textView.textColor = .black
}
}
// 커서 끝
func textViewdidEndEditing(_ textView: UITextView) {
// editing이 끝났는데 내용이 비어있다면 다시 Placeholder 있던 상태로 설정해줌
if textView.text.isEmpty {
textView.text = "이 곳에 줄거리를 남겨보세요."
textView.textColor = .lightGray
}
}