UIkit Basic 5 (UITabBarController, UICollectionView, NotificationCenter)
Updated:
UITabBar
앱에서 서로 다른 하위작업, 뷰, 모드 사이의 선택을 할 수 있도록, tabBar 하나 혹은 하나 이상의 버튼을 보여주는 컨트롤 입니다 (flutter 의 BottomNavigationBar 기능임
)
UITabController
다중 선택 인터페이스를 관리하는 컨테이너 뷰 컨트롤러로, 선택에 따라 어떤 자신 뷰 컨트롤러를 보여줄 것인지가 결정됩니다
탭바를 클릭할때마다 body 부분의 내용의 page 를 보여주는것을 컨트롤러를 통해서 변경합니다
UICollectionView
데이터 항목의 정렬된 컬렉션을 관리하고 커스텀한 레이아웃을 사용해 표시하는 객체 입니다
collectionView 는 리스트 형태로도 가능하고, 슬라이드와 같이 다양한 형태로 표현하는것이 가능합니다
예제코드
// 저장된 diaryList 를 FlowLayout() 화면에 구현
private func configureCollectionView() {
self.collectionView.collectionViewLayout = UICollectionViewFlowLayout()
self.collectionView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
UICollectionViewLayout
레이아웃의 객체를 통해 CollectionView 아이템을 시작적 스타일을 나타냅니다. 뷰의 위치를 결정하고, 시작정 상태의 정보를
UICollectionViewFlowLayout
cell 을 원하는 형태로 정렬할 수 있는데, flowLayout cell의 선형 경로로 배치 할 수 있습니다. 최대한 행의 cell 을 계속 채워 넣다가 다 차개 되면 새로운 행을 만들어서 배치해나갑니다
예제코드
// collectionView 의 layout 구성
extension ViewController: UICollectionViewDelegateFlowLayout {
// sizeForItemAt: size 를 설정하는 method 표시할 cell 의 사이즈를 CGSize 로 정의하고, return 해주면 설정한 size 대로 cell 에 표시됨
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// UIScreen.main.bounds.width 에 아이폰 사이즈 너비에 맞게 조절하고 / 2 해서 한 화면에 2개의 cell 이 나타나게 하며 -20 은 좌우 여백 10 의 합친 값이 20을 빼줘야 됨
return CGSize(width: (UIScreen.main.bounds.width / 2) - 20, height: 200)
}
}
flowLayout 을 사용하면 Grid 를 구현할 수 있지만, 더 다양한 모양으로도 구현할 수 있습니다
FlowLayout 의 구성요소
-
Flow 레이아웃 객체를 작성하고 컬렉션 뷰에 이를 할당합니다
-
셀의 width, height 을 정합니다 (required) - 지정하지 않으면, cell의 너비와 높이가 0 이되어 내용이 나타나지 않게 됨
-
필요한 경우 셀들 간의 좌우 최소 간격, 위아래 최소 간격을 설정하니다
-
section 에 header 와 footer 가 있다면 이것들의 크기를 지정합니다
-
레이아웃의 스크롤 방향을 설정합니다
FlowLayout cell 사이의 간격
FlowLayout 은 cell 사이의 간격을 조절할 수 있습니다. 설정하는 간격은 최소간격을 배치하는것에 따라 지정한 값보다 설정한 값이 클 수 도 있음니다.
만약 cell 들의 크기가 같게 되면 최소로 설정한 간격을 지킬 수 있지만, cell 들의 크기가 제각각 다르게 되면 실제 간격이 다를 수 있음에 주의해야 합니다
행과의 간격 이외에도, section 자체의 공간을 줄 수 있습니다. (margin 같은 개념)
UICollectionViewDataSource
컬렌션 뷰로 보여지는 콘텐츠들을 관리하는 객체입니다. DataSource 를 정의하기 위해서는 UICollectionView 의 프로토콜을 준수해야 하며, dataSource 의 역활을 collectionView 에 몇개의 sectionView 가 있는지 특정 cell 에 몇개의 셀이 있는지 셀에 contents 를 보여 주기 위해 collectionView 에 재공하게 됩니다
public protocol UICollectionViewDataSource: NSObjectProtocol {
// 지정된 섹션에 표시라 셀의 개수를 묻는 메서드 : numberOfItemsInSection
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
// 컬렉션뷰의 지정된 위치에 표시할 셀을 요청하는 메서드 : cellForItemAt
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
// 섹션의 개수를 묻는 메서드 : numberOfSections
optional func numberOfSections(in collectionView: UICollectionView) -> Int
}
UICollectionViewDelegate
contents 의 표현, 사용자의 상호작용과 관련된 것들을 관리하는 객체 입니다
- collectionView 와 관련된 핵심 객체들의 관계
// diary 화면에서 일기를 선택하였을때 일기 상세화면으로 이동하고, 일기 상세 내용을 볼 수 있습니다.
extension ViewController: UICollectionViewDelegate {
// 특정 cell 이 선택 되었음을 알리는 cell 임
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let viewController = self.storyboard?.instantiateViewController(identifier: "DiaryDetailViewController") as? DiaryDetailViewController else { return }
let diary = self.diaryList[indexPath.row]
viewController.diary = diary
viewController.indexPath = indexPath
// Notification Center 로 대체
// viewController.delegate = self
self.navigationController?.pushViewController(viewController, animated: true)
}
}
NotificationCenter
등록된 event 가 발생하면, 해당 event 들의 행동을 취하는것인데, 앱 내에서 어느곳에서나 메세지를 던지면, app 내 아무곳에서나 메세지를 받을 수 있는 역활입니다.
예제 코드
- NotificationCenter post event 생성
// .edit 일 경우에만 NotificationCenter post event 발생
switch self.diaryEditorMode {
case .new:
let diary = Diary(
uuidString: UUID().uuidString,
title: title,
contents: contents,
date: date,
isHeart: false)
self.delegate?.didSelectReigster(diary: diary)
case let .edit(indexPath, diary):
let diary = Diary(
uuidString: diary.uuidString,
title: title,
contents: contents,
date: date,
isHeart: diary.isHeart)
NotificationCenter.default.post(
name: NSNotification.Name("editDiary"),
object: diary,
userInfo: nil
)
// 더이상 userInfo 에 indexPath.row 값을 넘겨주지 않아도 됨 uuid 사용
// userInfo: ["indexPath.row" : indexPath.row])
}
self.navigationController?.popViewController(animated: true)
}
- NotificationCenter observer 생성
override func viewDidLoad() {
super.viewDidLoad()
self.configureCollectionView()
self.loadDiaryList()
// NotificationCenter editDiaryNotification observer
NotificationCenter.default.addObserver(
self,
selector: #selector(editDiaryNotification(_:)),
name: NSNotification.Name("editDiary"),
object: nil
)
// NotificationCenter isHeart observer
NotificationCenter.default.addObserver(
self,
selector: #selector(heartDiaryNotification(_:)),
name: NSNotification.Name("heartDiary"),
object: nil
)
// NotificationCenter deleteDiary observer
NotificationCenter.default.addObserver(
self,
selector: #selector(deleteDiaryNotification(_:)),
name: NSNotification.Name("deleteDiary"),
object: nil
)
}
// select 생성을 해줘야 함
// Notification editDiary observer selector
@objc func editDiaryNotification(_ notification: Notification) {
guard let diary = notification.object as? Diary else { return }
// 배열을 iteration 해서 전달 받은 uuid 와 같은 값이 배열의 요소에 있는지 확인하기, 있으면 해당 요소의 index를 return 받기
guard let index = self.diaryList.firstIndex(where: { $0.uuidString == diary.uuidString }) else { return }
self.diaryList[index] = diary
self.diaryList = self.diaryList.sorted(by: {
$0.date.compare($1.date) == .orderedDescending
})
self.collectionView.reloadData()
}
// Notification isHeart observer selector
@objc func heartDiaryNotification(_ notification: Notification) {
guard let heartDiary = notification.object as? [String: Any] else { return }
guard let isHeart = heartDiary["isHeart"] as? Bool else { return }
guard let uuidString = heartDiary["uuidString"] as? String else { return }
guard let index = self.diaryList.firstIndex(where: { $0.uuidString == uuidString }) else { return }
self.diaryList[index].isHeart = isHeart
}
// Notification deleteDiary observer selector
@objc func deleteDiaryNotification(_ notification: Notification) {
guard let uuidString = notification.object as? String else { return }
guard let index = self.diaryList.firstIndex(where: { $0.uuidString == uuidString }) else { return }
self.diaryList.remove(at: index)
self.collectionView.deleteItems(at: [IndexPath(row: index, section: 0)])
}
🔶 🔷 📌 🔑
reference
diary-ios-practice code - https://github.com/jacobkosmart/diary-ios-practice
김종권의 iOS 앱 개발 알아가기 - https://ios-development.tistory.com/103
fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal
Leave a comment