Navigation Bar
네비게이션 바는 화면 상단에 위치해 ios 앱에서 화면 간 이동을 관리해주는 UI 요소
빨간 박스 부분이 네비게이션 바. 타이틀 뿐만 아니라 버튼도 추가할 수 있다.
Navigation Bar 만드는 방법
규칙 - 네비게이션 컨트롤러에 임베드 하자
라이브러리로 네비게이션 bar를 화면에 넣을 순 있지만 그건 올바른 방법이 아니다.
스토리보드 - View Controller 클릭 - Embed In 메뉴 - Navigation Controller 클릭
그럼 오른쪽 사진과 같이 스토리보드에 Navigation Controller의 화면이 하나 더 생긴다
Navigation Controller에 Title 이라고 네비게이션 바가 자동으로 생성되고 오른쪽 화면에도 보이진 않지만 같은 위치를 눌러보면 파란색으로 활성화 되는 모습을 볼 수 있다.
Navigation Controller에서 Navigation Bar 클릭 - attribute inspector - Prefers Large Titles 체크
오른쪽 사진과 같이 큰 타이틀로 변경 된다.
타이틀 글자 설정
Navigation Controller에서 Navigation Bar 클릭 - attribute inspector - Title
여기에 원하는 타이틀을 적어주면 된다.
뷰 컨트롤러는 크게 2가지로 나뉜다.
Content View Controller
실제 UI 뷰 , 눈으로 보이는 걸 구성하는 컨트롤러
Container View Controller / Container
다른 뷰 컨트롤러를 관리하는 역할
여기에는 스토리보드에서 뷰를 추가할 수 없다
탭바 컨트롤러, 네비게이션 컨트롤러 등이 있다.
Navigation Controller
네비게이션 컨트롤러는 두가지 기능을 하고 있다.
1. 네비게이션 바를 추가하는 것
2. 눈으로 보이진 않지만 컨트롤러를 관리하는 것
추가 설명
새로운 화면을 표시하고 이전화면으로 돌아가는 것(뒤로가기)도 자동으로 처리
화면을 이동할때마다 네이게이션 바의 버튼과 타이틀을 자동으로 업데이트 (bar button item 자동 실행)
네비게이션바나 툴바같은 걸 빼면 그자체로 뭔가를 출력하진 않고
컨테이너가 표시되는 시점에 가장먼저 표시되는건 네비게이션 컨트롤러가 관리하고 있는 뷰컨트롤러의 화면
시뮬레이터를 구동시켰을 때 두번째 화면부터 뜨는 이유는
컨테이너는 관리하고 있는 뷰 컨트롤러를 가장 먼저 표시해주기 때문
push - 새로운 화면이 오른쪽에서 나타나는 것
pop - 새로운 화면이 왼쪽으로 사라지면서 이전 화면으로 돌아가는 것
코드에 쉽게 필수 메서드를 집어 넣는 방법
테이블 뷰에 필수 메서드를 입력하고 한다.
extension ViewController: UITableViewDataSource {
}
여기까지 입력하고 나서 UITableViewDataSource 프로토콜의 필수 메서드를 자동으로 입력하는 방법이다.
클래스 이름에 포커스(커서)가 있는 상태에서 shift command A 누르면 액션메뉴 나온다.
add라고 검색하면 add missing protocol requirements 클릭
그럼 문제 없이 입력되어 있을 것이다.
numberOfRowsSection의 리턴 값을 입력해주고
cellForRowAt의 코드를 입력해주면 된다.
재사용큐를 사용한다는 코드, 테이블 뷰에 표시할 내용, 리턴값 등을 입력해주면 된다.
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toDoList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = toDoList[indexPath.row]
return cell
}
}
새로운 화면으로 이동하기
모달 Modal
현재 화면 위에 새로운 화면을 덮어 씌워서 사용자와 상호작용을 하도록 하는 화면 전환 방식이다.
사용자가 모달을 닫기 전까지는 다른 화면과 상호작용이 불가능하다.
기존 화면과 독립적으로 동작하고 로그인, 설정변경 같은 특정작업에 집중하게 한다.
기본적으로는 아래
present - 네이게이션 바에서 버튼을 눌렀을 때 아래에서 위로 새로운 화면이 나타나는 것.
dismiss - 새로운 화면이 아래로 사라지는것
바에 버튼 추가하는 방법
스토리보드에서 라이브러리 - bar button item 드래그&드롭
bar button item 의 attribute inspector 에서 system item을 통해 아이템의 종류를 바꿀 수 있다.
bar button item 에서 system item 으로 추가한 버튼의 title은 시스템 언어설정에 따라 다르게 나타난다.
하지만 타이틀을 커스텀 하게 되면 그건 자동 번역 안된다.
Add 버튼을 눌렀을 때 새로운 화면 추가하기
스토리보드에서 라이브러리 (shift command L)- view controller를 빈 화면에 drag & drop
bar button item을 선택한 후 control 누른 채로 방금 추가한 view controller 화면으로 darg & drop
나타나는 Action Segue 메뉴에서 present Modally 선택 아래의 오른쪽 사진처럼 연결되는 모습을 볼 수 있다.
저 화살표는 세그웨이라고 한다.
inline title - 네비게이션 바에서 Add를 통해 나타난 새로 나타난 화면의 네비게이션바의 타이틀을 설정해줄 수 있다.
스토리 보드의 노치에 가려서 inline title이 가려서 안보이는데 시뮬레이터 실행하면 정상적으로 나온다고 한다.
두번째의 텍스트 필드에 추가한 값을 첫번째 화면의 테이블 뷰에 표시되도록 하는 방법
두번째 화면의 요소들을 뷰 컨트롤러에 추가해야하는데 두번째 화면부터는 class 를 직접 추가해줘야한다.
(첫번째는 xcode가 자동으로 만들어줬다.)
프로젝트 네비게이터에서 ViewController 우클릭 - New File from Template - cocoa Touch Class - Next
cocoa touch class - ios 앱을 만들 때 사용하는 프레임워크를 하나로 합쳐서 부르는 이름. mac os 는 cocoa
cocoa touch class 파일 만들때 주의할 점
화면과 연결하는 클래스는 view controller 클래스 라고 불리는데 이런 클래스 들은 규칙이 있다.
반드시 UIViewController를 직 간접적으로 상속해야 한다.
그리고 클래스 이름에도 규칙이 있는데 이름말미에 viewController 가 붙어야 된다. 일반적인 model class와 구분할 수 있게 하는게 좋다.
이렇게 클래스를 만든 다음 직접 연결 해줘야 된다.
identity inspector - class 에 복사해온 클래스의 이름을 붙여넣으면 연결이 된다.
Module의 이름이 변경되어있고 Inherit Module From Target 에 체크 되어있으면 연결이 되었다고 볼 수 있다.
command b를 눌러서 build 에 성공하면 정상적으로 연결되어있는 것이다.
class의 이름을 바꿀 때 팁
클래스 이름 우클릭, Refactor - Rename을 누르면
이렇게 창이 바뀌고 여기서 이름을 바꿔주면 한 번에 다 바뀐다.
코드 작성
이제 두번째 화면에 연결한 view controller에 cancel 버튼을 action으로 연결한 뒤
dismiss메서드를 넣어준다
class AddViewController: UIViewController {
@IBAction func cancel(_ sender: Any) {
dismiss(animated: true)
}
animated - true는 애니메이션 실행, false값을 넣으면 애니메이션 없이 바로 사라진다.
이제 save 버튼을 사용자가 눌렀을 때 첫번째 리스트에 표시하는 코드를 입력해보자
@IBAction func save(_ sender: Any) {
guard let text = inputField.text else {
showAlert(message: "내용을 입력해주세요", title: "알림")
return
}
dismiss(animated: true)
}
segue 세그웨이
스토리보드에서 화면을 연결하고 전환하는 기능을 한다.
연결된 화면을 자동으로 만들고 적절한 방식으로 새로운 화면에 표시해준다.
새로운 화면에 표시하기 직전에 메서드를 하나 호출
호출하는 위치는 새로운 화면이 아니라 바로 이전 화면에서 메서드를 호출한다.
- 2번째 화면을 만든다음에 화면을 표시하기 직전에 1번째 화면에서 호출하는 것이다.
그래서 1번째 화면에 메서드를 입력해주면 된다.
스토리보드에서 봤을 때 세그웨이 기준 왼쪽은 Source, 오른쪽은 Destination이라고 한다.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
<#code#>
}
prepare 메서드를 이용해 대이터를 전달 할 수 있다.
sefue.identifier를 사용해서 특정 세그웨이를 구분하고 segue.destination으로 목적지인 뷰 컨트롤러에 접근한다.
보다시피 세그웨이를 선택하고 attribute inspector를 보면 Class 에 UIStroryboardSegue 라고 써져있는 걸 볼 수 있다.
다음에 커스텀 세그웨이를 만들게 되면 Class 칸에 입력하면 된다고 한다.
이전 화면으로 값을 전달하는 방법
첫번째. 뷰 컨트롤러를 속성으로 연결하는 방법
AddViewController에 코드 작성
class AddViewController: UIViewController {
var listVC: ListViewController?
}
클래스에 있는 저장 속성은 기본값으로 저장하거나 생성자를 통해서 적절한 값으로 초기화 해야되는데
옵셔널로 선언하지 않으면 두가지 모두 지켜지지 않고 있기 때문에 에러가 발생한다. 옵셔널로 선언
이전 화면의 인스턴스를 저장해야하는데 언제 저장해야할까?
세그웨이를 통해 저장한다고 한다.
ListViewController에 코드 작성
class ListViewController: UIViewController {
var toDoList = [String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? AddViewController {
vc.listVC = self
}
}
이렇게 입력해주면 된다.
.destination을 옵션 클릭해서 타입을 확인하면 UIViewController 타입이라고 나온다.
이 segue의 오른쪽의 뷰 컨트롤러는 AddViewController이다.
AddViewController의 인스턴스가 UIViewController타입으로 업캐스팅 되어 있다는 거라고 한다.
속성에 접근하려면 다운캐스팅을 해야한다.
다음시간에 업캐스팅과 다운캐스팅을 공부하기로 했다.
그냥 따라 적었는데 as?는 타입캐스팅 연산자라고 한다.
리스트 속성에 접근해서 self로 저장하면 되고 여기서 self는 ListViewController 라고 한다.
여기에 저장된 self가 AddViewController에 저장된다. (정확히는 AddViewController에서 선언한 listVC에)
이제 접근할 수 있게 되었다.
여기에 문제가 있다.
segue의 destination이 두번째 화면이 아닌 navigation controller를 거쳐서 두번째 화면에 접근해야한다.
그러니까 if let vc = segue.destination에서 navigation controller가 저장되어 있다.
연결된 뷰 컨트롤러들을 관리하는데
children.first 속성으로 접근하면 된다고 한다.
ListViewController에서 코드 추가
class ListViewController: UIViewController {
var toDoList = [String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination.children.first as? AddViewController {
vc.listVC = self
}
}
이제 AddViewController에서
@IBAction func save(_ sender: Any) {
guard let text = inputField.text else {
showAlert(message: "내용을 입력해주세요", title: "알림")
return
}
listVC?.toDoList.append(text)
listVC?.toDoTableView.reloadData()
dismiss(animated: true)
}
이렇게 listVC?.toDoList.append(text)라고 추가로 입력해주면 된다.
reloadData() 를 호출하여 테이블 뷰에 새로운 데이터가 있다는 걸 알려주는 코드까지 넣으면 완성이다.
두번째, 세번째 방법은 다음 게시글을 통해서 적어야 겠다.
추가로 많이 하는 실수이면서 하면 안되는 방법
AddViewController 에서 ListViewController의 속성을 가져와서 저장하는 방법은 잘못된 방법이다.
이게 무슨 말이냐면
let vc = ListViewController()
vc.toDoList.append(text)
이렇게 AddViewController에 입력하면 문제없이 Build 되기는 하지만
안된다고 한다. ListViewController에 접근은 할 수는 있지만 그 반대는 안된다고 한다.
'IT > 프로그래밍 노트' 카테고리의 다른 글
031 optional chaining (0) | 2025.03.24 |
---|---|
013 함수표기법 (0) | 2025.03.23 |
012 문법최적화, 클로저 단축 문법 (0) | 2025.03.20 |
029 TextFieldDelegate 숫자 입력 검증, guard문 (0) | 2025.03.20 |
011 Function Type, Closure Type, 함수자료형, 클로저 자료형 (0) | 2025.03.19 |