네비게이션 바 Large Title & UIPageViewController
정리가 안된 상태에서 이것저것 해보다보니까 엄청 엉켜서 헤맸다..
정리하고, 차근차근해보니까 성공 :)
일단 UIPageViewController
UIPageViewController
: 하위 뷰 컨트롤러가 각 페이지를 관리하는 콘텐츠 페이지 간의 탐색을 관리하는 컨테이너 뷰 컨트롤러
https://developer.apple.com/documentation/uikit/uipageviewcontroller
탭바 없이 진행해보자는 팀원들의 의견을 받아서 !
양 옆으로 스크롤해서 이동하고 페이지 컨트롤러를 밑에 작게 만들어서 페이지 이동을 하게 하기로 했다 ㆅㆅ
찾아봤더니 Scroll 뷰로 직접 만드는 방법이랑 페이지 컨트롤러 사용해서 만드는 방법이 있는데,
내가 만들 앱은 탐색페이지 / 내 페이지 / 설정 페이지로 나눌 것이기 때문에 각 뷰컨트롤러를 페이지에 넣을 수 있게 분리되어있는 구조가 좋을 것 같다고 생각하기도 했고, 스크롤뷰로 만들면 그 페이지마다 스크롤 크기랑 이것저것 맞춰줘야할 게 많다고 해서.. 그냥 기본적으로 제공되는 PageViewController를 사용하기로 했다 !
구조는 PageViewController 안에 ExploreViewController, MyTripsViewController, MyPageViewController가 들어가는 방식!
func setupPVC() {
//UIPageViewController 설정
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
pageViewController.dataSource = self
pageViewController.delegate = self
//첫번째 페이지 뷰컨 설정
if let initialViewController = viewControllerAtIndex(index: 0) {
pageViewController.setViewControllers([initialViewController], direction: .forward, animated: true, completion: nil)
}
// PageViewController를 부모 뷰에 추가
addChild(pageViewController)
view.addSubview(pageViewController.view)
pageViewController.didMove(toParent: self)
if let scrollView = pageViewController.view.subviews.compactMap({ $0 as? UIScrollView }).first {
scrollView.panGestureRecognizer.addTarget(self, action: #selector(handlePanGesture(_:)))
}
pageViewController.view.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
여기 설정에서 transitionStyle을 변경해주면 넘기는 스타일도 변경할 수 있어서 신기했다 !
이 페이지 컨트롤러를 사용하면 밑에 동그라미로 자기 페이지 위치를 알려주는 그런 것들도 기본으로 제공해서 사용할 수 있다고 한다 !
여튼 이렇게 페이지 컨트롤러를 세팅해주고,
func viewControllerAtIndex(index: Int) -> UINavigationController? {
guard index >= 0 && index < pageContent.count else {
return nil
}
let contentViewController: UIViewController
if index == 0 {
contentViewController = ExploreViewController()
} else if index == 1 {
contentViewController = MyTripsViewController()
} else {
contentViewController = MyPageViewController()
}
if let exploreVC = contentViewController as? ExploreViewController {
exploreVC.pageIndex = index
exploreVC.pageText = pageContent[index]
} else if let myTripsVC = contentViewController as? MyTripsViewController {
myTripsVC.pageIndex = index
myTripsVC.pageText = pageContent[index]
} else if let myPageVC = contentViewController as? MyPageViewController {
myPageVC.pageIndex = index
}
let navigationController = UINavigationController(rootViewController: contentViewController)
navigationController.navigationBar.prefersLargeTitles = true
navigationController.navigationBar.tintColor = .font
navigationController.navigationBar.largeTitleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 34, weight: .bold)
]
navigationController.navigationBar.titleTextAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20, weight: .regular)
]
// 하단 선 제거
navigationController.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController.navigationBar.shadowImage = UIImage()
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = .systemBackground
appearance.backgroundEffect = nil
navigationController.navigationBar.standardAppearance = appearance
return navigationController
}
우리는 라지 타이틀을 사용할거라서 이렇게 부모뷰인 PageViewController에서 전체적인 네비게이션 컨트롤러 설정 ( +라지타이틀)
* 페이지 컨트롤러로 화면 이동 시에 맨 끝 화면에서도 스크롤되는 이슈!
맨 끝화면에서는 굳이 왼쪽으로 스크롤하면 모션을 줄 필요가 없어서 이렇게 코드로 모션을 제거해 줬다
@objc func handlePanGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
if let scrollView = gestureRecognizer.view as? UIScrollView {
let translation = gestureRecognizer.translation(in: scrollView)
// 네비게이션 이동 시 스크롤 비활성화
if !isScrollEnabled {
gestureRecognizer.isEnabled = false
gestureRecognizer.isEnabled = true
return
}
// 첫 번째 페이지에서 왼쪽으로 스크롤할 때 스크롤을 막음
if selectedIndex == 0 && translation.x > 0 {
gestureRecognizer.isEnabled = false
gestureRecognizer.isEnabled = true
}
// 마지막 페이지에서 오른쪽으로 스크롤할 때 스크롤을 막음
else if selectedIndex == pageContent.count - 1 && translation.x < 0 {
gestureRecognizer.isEnabled = false
gestureRecognizer.isEnabled = true
}
}
}
* UIPageViewControllerDelegate 프로토콜은 페이지 전환과 관련된 다양한 이벤트를 처리
* UIPageViewControllerDataSource 프로토콜은 페이지 컨트롤러가 어떤 뷰 컨트롤러를 표시할지를 결정
extension PageViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let navigationController = viewController as? UINavigationController,
let viewController = navigationController.topViewController as? UIViewController & PageIndexed,
let index = viewController.pageIndex, index > 0 else {
return nil
}
return viewControllerAtIndex(index: index - 1)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let navigationController = viewController as? UINavigationController,
let viewController = navigationController.topViewController as? UIViewController & PageIndexed,
let index = viewController.pageIndex, index < pageContent.count - 1 else {
return nil
}
return viewControllerAtIndex(index: index + 1)
}
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
if completed, let viewController = pageViewController.viewControllers?.first as? UINavigationController,
let pageIndexed = viewController.topViewController as? PageIndexed, let pageIndex = pageIndexed.pageIndex {
selectedIndex = pageIndex
NotificationCenter.default.post(name: .didChangePage, object: nil, userInfo: ["selectedIndex": selectedIndex])
}
}
}
* PageViewController 라는 페이지 컨트롤러의 역할을 하는 ViewController가 있고, 하위에 실제 컨텐츠들을 가진 자식뷰컨들이 페이지에 하나씩 들어간다 ! 나는 이 개념이 잘 안 잡혀서 어려웠다.. 지금와서 생각해보면 간단한데,, !!ㅠㅠ
라지타이틀은 너무 예쁘지만 다루기가 좀 까다로운 것 같다..
- 여백을 주기가 어렵다. (불가능할지도..)
라지타이틀에 왼쪽에 여백을 주는 것은 불가능한 거 같다.. 엄청 찾아보고 도전해봤는데 실패했다..
-> 큰 제목에는 여백을 줄 수 없고, 작은 제목이 되었을 때는 줄 수 있는 거 같음 !
- 네비게이션이 동적으로 작아졌다가 커졌다가 하기 때문에 가끔씩 알 수 없는 오류를 마주할 때가 있다..
그리고 이건 라지타이틀에 버튼 넣기 구현 :)
- 라지타이틀은 기본적으로 네비게이션 바가 두줄로 들어가는 거 같다 ! 그래서 그냥 네비게이션바에 rightItem으로 넣으면 타이틀은 두번째 줄에 있는데 버튼아이템은 첫번째 줄에 생긴다..
만약에 그냥 넣으면 저 오리 위치에 버튼이 생긴다 ! 그래서 커스텀해줘야 한다.. ㆅㆅ
lazy var plusButton: UIButton = {
let button = UIButton(type: .system)
let imageConfig = UIImage.SymbolConfiguration(pointSize: 15, weight: .regular)
let image = UIImage(systemName: "plus", withConfiguration: imageConfig)
button.setImage(image, for: .normal)
button.tintColor = UIColor(named: "textColor")
button.backgroundColor = .font
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
return button
}()
이렇게 플러스 버튼을 만들어 놓고
private func setupNV() {
navigationItem.title = pageText
if let navigationBarSuperview = navigationController?.navigationBar.superview {
navigationBarSuperview.addSubview(plusButton)
plusButton.snp.makeConstraints {
$0.trailing.equalTo(navigationController!.navigationBar.snp.trailing).offset(-16)
$0.bottom.equalTo(navigationController!.navigationBar.snp.bottom).offset(-10)
$0.size.equalTo(CGSize(width: 30, height: 30))
}
}
}
plsuButton을 addSubview해서 네비게이션바에 넣어주면 된다 ! 위치도 맞춰줄 수 있다!
그리고 이번에 네비게이션 바에 버튼을 넣으면서
let imageConfig = UIImage.SymbolConfiguration(pointSize: 15, weight: .regular)
let image = UIImage(systemName: "plus", withConfiguration: imageConfig)
button.setImage(image, for: .normal)
이 방식으로 버튼 사이즈를 많이 조정해줬던 거 같음 !
그리고 막상 실행해보면 내가 설정한 사이즈대로 나오지 않는 경우도 많아서 레이아웃이 완전히 완료된 후에 프레임을 확인할 수 있도록
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Button width: \(searchButton.frame.size.width), height: \(searchButton.frame.size.height)")
}
이런 식으로 프린트해보면 실제 크기를 알아보고 크기를 조정할 수 있다..
사실 나도 확실하게 잘은 몰라서..
저기 위에 코드도 보면 위에서 한 번 포인트 사이즈를 정해주고, 또 밑에서 CGSize를 정해주는데 이게 맞나.. 싶기도 하고,, 잘 모르겠다..
더 공부해봐야지... 화이팅...
'TIL' 카테고리의 다른 글
SwiftUI _ ViewModel (@Published, @ObservedObject, @StateObject) (1) | 2024.07.22 |
---|---|
61일차 TIL _ 팀프로젝트 회고 (4) | 2024.05.25 |
59일차 TIL (5) | 2024.05.22 |
58일차 TIL (0) | 2024.05.21 |
57일차 TIL (0) | 2024.05.20 |