기어가더라도 제대로

[UIKit-기초] Navigation 상단바 영역 다루기, color navigation bar, change back button, color back button, color navigation title(UINavigationBar, StatusBar, when scrolling, transparent navigation bar) 본문

UIKit 기초

[UIKit-기초] Navigation 상단바 영역 다루기, color navigation bar, change back button, color back button, color navigation title(UINavigationBar, StatusBar, when scrolling, transparent navigation bar)

Damagucci-juice 2025. 2. 2. 17:42

UIKit도 나온지 10년이 넘었고, 스택오버플로우도 거의 object-c가 혼재하던 시절에 나온 코드 자료가 많아서, 한번 정리를 해놓으려 함

편의상 sceneDelegate나 View Controller에서 하지만, Appearance를 편집하는 매니저에서 바꾸는 것을 추천함

  • 그 이유는 나중에 뷰컨이 많아지면 어느 뷰컨에서 어떻게 설정을 바꿨는지 알기가 어려워서, 효과들이 중첩되는데 이거가 한번 꼬이면 어느 뷰에서 어떻게 바꿨는지 찾는게 더 오래걸림 

사전 설정

  • SnapKit: Auto layout을 편하게 하기 위해 사용
  • Then: UI 선언을 편하게 하기 위해 사용
  • 코드 베이스 UI: 스토리 보드를 사용하지 않을 것임
  • FirstViewController와 SecondViewController를 생성하고 각각 배경색과 버튼, 레이블 같은것을 추가함
import UIKit
import Then
import SnapKit

class FirstViewController: UIViewController {

    let button = UIButton().then {
        $0.setTitle("FirstViewController then move to Second", for: .normal)
        $0.setTitleColor(.blue, for: .normal)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(button)

        button.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }

        button.addTarget(
            self,
            action: #selector(moveToSecondTapped),
            for: .touchUpInside
        )
    }

    @objc func moveToSecondTapped() {
        print("tapped")
    }


}

class SecondViewController: UIViewController {

    let label = UILabel().then {
        $0.text = "Second View Controller"
        $0.textColor = .black
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        view.addSubview(label)

        label.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
    }
}

 

전역적으로 네비게이션 배경색을 변경하기

  • SceneDelegate에서 네비게이션 영역을 편집

 

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // init windows
        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        let navigationController = UINavigationController(rootViewController: FirstViewController())

        // navigation appearance
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .systemBrown

        // set values
        navigationController.navigationBar.standardAppearance = appearance
        navigationController.navigationBar.compactAppearance = appearance
        navigationController.navigationBar.scrollEdgeAppearance = appearance

        // activate window with root view controller
        window.rootViewController = navigationController
        self.window = window
        self.window?.makeKeyAndVisible()
    }

 

특정 View Controller에서 Navigation Bar 영역을 편집하기

  • SceneDelegate의 Scene 함수에서 Navigation Appearance를 조작하던 곳을 주석 처리
// chanage globally
/*
// navigation appearance
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .systemBrown

// set values
navigationController.navigationBar.standardAppearance = appearance
navigationController.navigationBar.compactAppearance = appearance
navigationController.navigationBar.scrollEdgeAppearance = appearance
*/
  • FirstViewController에서 네비게이션 영역 색상을 초록색으로 변경
    • 전역적으로 변경하는 것과 달리 국지적으로 변경하는 것은 꼭 리셋을 해주는게 나중에 편함
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // set naivigation background color
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        appearance.backgroundColor = .systemGreen

        if let navigationBar = navigationController?.navigationBar {
            navigationBar.standardAppearance = appearance
            navigationBar.compactAppearance = appearance
            navigationBar.scrollEdgeAppearance = appearance
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        // reset naivigation background color
        if let navigationBar = navigationController?.navigationBar {
            navigationBar.standardAppearance = .init()
            navigationBar.compactAppearance = nil
            navigationBar.scrollEdgeAppearance = nil
        }
    }
  • 버튼에 다음 뷰컨트롤러로 넘어가는 액션을 추가
// view did load
button.addTarget(
    self,
    action: #selector(moveToSecondTapped),
    for: .touchUpInside
)


@objc func moveToSecondTapped() {
    navigationController?.pushViewController(SecondViewController(), animated: false)
}
  • 구현 모습

 

타이틀 색상 변경하기

  • 위 gif에서 배경이 초록인데 타이틀 색상이 검정이라 이쁘지 않음, 흰색으로 색칠하기
// FirstViewController.viewWillAppear(animated:)
// change background color
appearance.backgroundColor = .systemGreen

// change title color
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]

 

상단바 영역 투명하게 하기

  • SecondViewController에서 화면 상단에 200 높이 짜리 컬러뷰 선언 
class SecondViewController: UIViewController {

    let colorView = UIView().then {
        $0.backgroundColor = .systemGray
    }

    let label = UILabel().then {
        $0.text = "Second View Controller"
        $0.textColor = .black
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        title = "Second View Controller"

        // setup button
        view.addSubview(label)
        label.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }

        // setup color view
        view.addSubview(colorView)
        colorView.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview()
            make.height.equalTo(200)
        }
    }
  • Appearance 투명하게 설정
// MARK: - SecondViewController
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    let appearance = UINavigationBarAppearance()
    // set navigation transparent background
    appearance.configureWithTransparentBackground()
    // change title color
    appearance.titleTextAttributes = [.foregroundColor: UIColor.white]


    // set values
    if let navigationBar = navigationController?.navigationBar {
        navigationBar.standardAppearance = appearance
        navigationBar.compactAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
    }
}

  • 근데 backbutton이 마음에 안듬, 색상이 매치가 안됨, backbutton 색상도 바꾸기
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let appearance = UINavigationBarAppearance()
        // set navigation transparent background
        appearance.configureWithTransparentBackground()
        // change title color
        appearance.titleTextAttributes = [.foregroundColor: UIColor.white]

        // change back button appearance
        let buttonAppearance = UIBarButtonItemAppearance(style: .plain)
        /// backbutton title color
        buttonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.white]
        appearance.buttonAppearance = buttonAppearance

        /// backbutton tint color
        let image = UIImage(systemName: "chevron.backward")?.withTintColor(.white, renderingMode: .alwaysOriginal)
        appearance.setBackIndicatorImage(image, transitionMaskImage: image)

        // set values
        if let navigationBar = navigationController?.navigationBar {
            navigationBar.standardAppearance = appearance
            navigationBar.compactAppearance = appearance
            navigationBar.scrollEdgeAppearance = appearance
        }
    }

상단 바 영역을 투명하게 하다가 스크롤을 하면 불투명하게 바꾸기

  • 스크롤뷰 + 컨텐츠 스택뷰를 생성
class SecondViewController: UIViewController {

    let scrollView = UIScrollView()

    let contentStackView = UIStackView().then {
        $0.axis = .vertical
        $0.distribution = .fill
        $0.alignment = .fill
        $0.spacing = 20
    }

    let colors = [UIColor.systemBlue, UIColor.systemOrange, UIColor.systemGreen, UIColor.systemRed]
override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        title = "Second View Controller"

        // setup scroll view
        view.addSubview(scrollView)
        scrollView.addSubview(contentStackView)
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        /// 스크롤 뷰를 상단 영역까지 꽉 채우기
        scrollView.contentInsetAdjustmentBehavior = .never

        // setup contentStackView
        contentStackView.snp.makeConstraints { make in
            make.edges.width.equalToSuperview()
        }
        colors.forEach { color in
            let coloredView = UIView().then {
                $0.heightAnchor.constraint(equalToConstant: 500).isActive = true
                $0.backgroundColor = color
            }

            contentStackView.addArrangedSubview(coloredView)
        }
    }
  • 일반 Appearacne와 스크롤 중일 때 Appearance를 별도로 설정
        // MARK: - transparent
        let transparentAppearance = UINavigationBarAppearance()
        transparentAppearance.configureWithTransparentBackground()
        // title color
        transparentAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
        // button title color
        let buttonAppearance = UIBarButtonItemAppearance(style: .plain)
        buttonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.white]
        transparentAppearance.buttonAppearance = buttonAppearance
        // button tint color
        let image = UIImage(systemName: "chevron.backward")?.withTintColor(.white, renderingMode: .alwaysOriginal)
        transparentAppearance.setBackIndicatorImage(image, transitionMaskImage: image)

        // MARK: - opaque
        let opaqueAppearance = UINavigationBarAppearance()
        opaqueAppearance.configureWithOpaqueBackground()
        // title color
        opaqueAppearance.titleTextAttributes = [.foregroundColor: UIColor.label]
        // button title color
        let scrollButtonAppearance = UIBarButtonItemAppearance(style: .plain)
        scrollButtonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.black]
        opaqueAppearance.buttonAppearance = scrollButtonAppearance
        // button tint color
        let scrollBackbuttonImage = UIImage(systemName: "chevron.backward")?.withTintColor(.label, renderingMode: .alwaysOriginal)
        opaqueAppearance.setBackIndicatorImage(scrollBackbuttonImage, transitionMaskImage: scrollBackbuttonImage)
  • 최상단일 때 투명, 스크롤 중일 때 불투명하게 옵션 설정
// MARK: - SecondViewController
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // 생략 ~~
    
    // set values
    if let navigationBar = navigationController?.navigationBar {
        // set opaque type when top edge
        navigationBar.scrollEdgeAppearance = transparentAppearance
        // set transparent type when scrolling
        navigationBar.standardAppearance = opaqueAppearance
    }
}
  • 완성 화면

 

전체 코드

https://github.com/Damagucci-Juice/ColoringNavigationArea

 

GitHub - Damagucci-Juice/ColoringNavigationArea

Contribute to Damagucci-Juice/ColoringNavigationArea development by creating an account on GitHub.

github.com

 

참조

- 백버튼 이미지 변경: https://sarunw.com/posts/how-to-change-back-button-image/

 

How to change a back button image | Sarunw

Learn how to change a UINavigationBar back button indicator.

sarunw.com

- 상단 영역 색칠: https://nemecek.be/blog/150/customizing-the-navigation-bar-in-uikit

 

Customizing the navigation bar in UIKit

What worked for me and what didn’t.

nemecek.be

- 백버튼 이미지 색상 변경, "OlegKorchitskiy" 답변: https://developer.apple.com/forums/thread/709517

 

NavigationBar Back Button Color iO… | Apple Developer Forums

I just noticed, that iOS 16 is using the accent color for the navigation bar back button. (SwiftUI) When running my app on iOS 15 devices it's white. Is there a way to change that behavior? I want to have another accent color than white.

developer.apple.com

 

Comments