VesselWheel

programmatically NavigationController 만들기(feat. NavigationItem, toolbar, UINavigationControllerDelegate) 본문

Xcode Study

programmatically NavigationController 만들기(feat. NavigationItem, toolbar, UINavigationControllerDelegate)

JasonYang 2024. 1. 31. 10:39

NavigationController 란?

더보기

Navigation Controller

UINavigationController 기초

  • 화면 간 탐색 제공: UINavigationController는 iOS 앱에서 화면 간의 탐색을 관리하며, 스택 구조로 화면을 쌓아 올립니다.
  • Root View Controller: UINavigationController의 초기 화면으로 설정되는 뷰 컨트롤러를 Root View Controller라고 합니다.
  • Push와 Pop: UINavigationController는 pushViewController(_:animated:) 메서드를 사용하여 새로운 화면을 스택에 추가하고, popViewController(animated:) 메서드를 사용하여 이전 화면으로 돌아갑니다.

UINavigationController 속성

  • NavigationBar: UINavigationController는 화면 위에 나타나는 Navigation Bar를 제공하며, 이는 타이틀, 버튼 등을 포함할 수 있습니다.
  • Toolbar: UINavigationController는 Navigation Bar 아래에 추가로 Toolbar를 제공할 수 있습니다.

NavigationItem

  • Title: 각각의 뷰 컨트롤러는 navigationItem.title 속성을 통해 Navigation Bar에 표시되는 제목을 설정할 수 있습니다.
  • LeftBarButtonItems 및 RightBarButtonItems: 네비게이션 바의 왼쪽과 오른쪽에 버튼을 추가하여 사용자 상호 작용을 지원할 수 있습니다.

UINavigationController의 고급 사용법

  • Custom Transition Animation: UINavigationController의 화면 전환 애니메이션을 사용자 지정할 수 있습니다.
  • Delegate Pattern 활용: UINavigationControllerDelegate 프로토콜을 채택하여 화면 전환에 대한 이벤트를 처리하거나 커스텀 동작을 추가할 수 있습니다.
  • Modal Presentation: UINavigationController는 다른 뷰 컨트롤러를 Modal로 표시할 수 있습니다.
  • TabBarController와의 통합: UINavigationController는 UITabBarController와 함께 사용하여 복잡한 앱 구조를 구성할 수 있습니다.

주의사항

  • 메모리 관리: UINavigationController는 자동으로 뷰 컨트롤러를 관리하며, 메모리 관리에 유의해야 합니다.
  • 스크롤뷰와 함께 사용: UINavigationController에 포함된 뷰 컨트롤러가 스크롤뷰를 가지고 있을 경우, 스크롤이 Navigation Bar와 겹칠 수 있습니다. 이 경우에는 오토레이아웃을 통해 조정이 필요합니다.

1. 우선 storyboard 관련 사항을 삭제 한다. (이전 글 참조)

 

2. SceneDelegate에서 NavigationController 등록

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let windowScene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: windowScene)
        let navc = UINavigationController(rootViewController: ViewController())
        window?.rootViewController = navc
        window?.makeKeyAndVisible()
    }

UINavigationController가 지정된 View Hierarchy

3. rootViewController인 ViewController에서 navigationItem을 활용하여 SecondViewController으로 화면 전환 구현 코드

//
//  ViewController.swift
//  variableAutoLayout
//
//  Created by Jason Yang on 1/24/24.
//

import UIKit

class ViewController: UIViewController {
    var centerConstraint: NSLayoutConstraint?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "First View"
        view.backgroundColor = .lightGray
//        navigationController?.pushViewController(FirstViewController, animated: true)
//        navigationController?.popToViewController(separateSecondaryView, animated: true)
//        navigationItem.rightBarButtonItem = UIBarButtonItem()
//        navigationItem.leftBarButtonItem
        
//        present(UIViewController, animated: true)
        let nextButton = UIButton(type: .system)
        nextButton.setTitle("Next", for: .normal)
        nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside)
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Pop", style: .plain, target: self, action: #selector(popButtonTapped))
        
        view.addSubview(nextButton)
        
        nextButton.translatesAutoresizingMaskIntoConstraints = false
        nextButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        nextButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        
//        let button = UIButton(type: .system)
//        button.setTitle("Centered Button", for: .normal)
//        button.translatesAutoresizingMaskIntoConstraints = false
//        view.addSubview(button)
//        
//        // 제약 조건을 변수에 할당
//        centerConstraint = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
//        centerConstraint?.isActive = true
//        
//        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
//        
//        // 버튼을 탭할 때마다 중앙에 위치하도록 변경
//        button.addTarget(self, action: #selector(centerButtonTapped), for: .touchUpInside)
    }
    
    @objc func nextButtonTapped() {
        let secondViewController = SecondViewController()
        navigationController?.pushViewController(secondViewController, animated: true)
    }
    
    @objc func popButtonTapped() {

        navigationController?.popViewController(animated: true)
    }
    
//    @objc func centerButtonTapped() {
//        // 버튼 중앙 제약 조건을 변경하여 애니메이션
//        UIView.animate(withDuration: 0.5) {
//            if let currentConstant = self.centerConstraint?.constant {
//                self.centerConstraint?.constant = (currentConstant == 0) ? 100 : 0
//                self.view.layoutIfNeeded()
//            }
//        }
//    }
}


class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
        view.backgroundColor = .cyan

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Pop", style: .plain, target: self, action: #selector(popButtonTapped))
    }

    @objc func popButtonTapped() {
        navigationController?.popViewController(animated: true)
    }
}

NavigationItem으로 연결된 ViewControllers

 

4. Toolbar로 NavigationController 연결하기 

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "First View"
        view.backgroundColor = .white

        let nextButton = UIButton(type: .system)
        nextButton.setTitle("Next", for: .normal)
        nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside)

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Toolbar", style: .plain, target: self, action: #selector(toolbarButtonTapped))

        view.addSubview(nextButton)

        nextButton.translatesAutoresizingMaskIntoConstraints = false
        nextButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        nextButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

    @objc func nextButtonTapped() {
        let secondViewController = SecondViewController()
        navigationController?.pushViewController(secondViewController, animated: true)
    }

    @objc func toolbarButtonTapped() {
        // 툴바 버튼을 누르면 새로운 화면 푸시
        let toolbarViewController = ToolbarViewController()
        navigationController?.pushViewController(toolbarViewController, animated: true)
    }
}

class ToolbarViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Toolbar View"
        view.backgroundColor = .yellow

        // 툴바 버튼
        let backButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(backButtonTapped))

        // 툴바에 버튼 추가
        toolbarItems = [backButton]

        // 툴바 보이기
        navigationController?.isToolbarHidden = false
    }

    @objc func backButtonTapped() {
        // 툴바의 뒤로가기 버튼을 누르면 이전 화면으로 돌아감
        navigationController?.popViewController(animated: true)
    }
}


class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
        view.backgroundColor = .cyan

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Pop", style: .plain, target: self, action: #selector(popButtonTapped))
    }

    @objc func popButtonTapped() {
        navigationController?.popViewController(animated: true)
    }
}

Toolbar로 연결된 Controllers

5. UINavigationControllerDelegate 활용하기

import UIKit

class FirstViewController: UIViewController, UINavigationControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "First View"
        view.backgroundColor = .white

        let nextButton = UIButton(type: .system)
        nextButton.setTitle("Next", for: .normal)
        nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside)

        view.addSubview(nextButton)

        nextButton.translatesAutoresizingMaskIntoConstraints = false
        nextButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        nextButton.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

        // 델리게이트 설정
        navigationController?.delegate = self
    }

    @objc func nextButtonTapped() {
        let secondViewController = SecondViewController()
        navigationController?.pushViewController(secondViewController, animated: true)
    }

    // MARK: - UINavigationControllerDelegate

    // 화면이 푸시될 때 호출되는 메서드
    func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
        print("Will Show: \(viewController)")
    }

    // 화면이 팝될 때 호출되는 메서드
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        print("Did Show: \(viewController)")
    }
}

class SecondViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Second View"
        view.backgroundColor = .cyan
    }
}

콘솔화면에 나온, navigationController?.delegate = self 활용한 화면이 구현될 때 호출되는 매서드