[SwiftUI] tabBar, navigationBar height 알아내기

정유진·2023년 2월 17일
0

swift

목록 보기
16/25
post-thumbnail
post-custom-banner

🤔 들어가며

subview로 사용할 view의 height를 superview.height - (tabbar.height + navigationbar.height) 로 설정하고 싶었다. UIKit이라면 각각의 viewController에 접근하여 각각의 bound 또는 size를 알아낼 수 있는데 SwiftUI에서는 알아낼 방법이 요원했기에 약간의 편법을 사용했다.

🗒️ 미리 알아둘 것

UIViewControllerRepresentable

protocol UIViewControllerRepresentable : View where Self.Body == Never
  • SwiftUI에서 UIViewController 객체를 생성하고 관리하기 위해 사용된다.
  • create, update, teardown 메서드를 구현할 수 있다.
    • makeUIViewController(context: Context) -> Self.UIViewControllerType
    • updateUIViewController(Self.UIViewControllerType, context: Self.Context)
    • dismantleUIViewController(Self.UIViewControllerType, coordinator: Self.Coordinator)
  • param: context는 system이 생성해서 넘겨주는 parameter이고 현재 state에 관한 정보를 갖고 있다.
  • return: 내가 custom한 viewController를 리턴하여 SwiftUI의 인터페이스를 만드는 viewController를 바꿔치기할 수 있다.

modifier: background(alignment:content:)

func background<V>(
    alignment: Alignment = .center,
    content: () -> V
) -> some View where V : View
  • parameter로 넘긴 content(view)를 background view로 사용할 수 있다.
  • swiftUI 코드에서 UIViewControllerRepresentable에 접근하기 위해 사용할 것이다.

1. tabBar height 알아내기

Tabbar proxy controller

struct TabBarProxy: UIViewControllerRepresentable {
    var callback: (UIView, UITabBar) -> Void
    private let proxyController = MyViewController()
    
    func makeUIViewController(context: Context) -> UIViewController {
        proxyController.callback = callback
        return proxyController
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print("update controller")
    }
    
    typealias UIViewControllerType = UIViewController
    
    private class MyViewController: UIViewController {
        var callback: (UIView, UITabBar) -> Void = { _, _ in }
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            // the nearest ancestor in the view controller hierarchy
            if let tabview = self.tabBarController {
                // callback 실행
                self.callback(tabview.view, tabview.tabBar)
            }
        }
    }
}
  • UIViewController를 따르는 MyViewController class 생성
  • property인 callback은 UIView와 UITabBar를 인자로 받는 클로저
  • 이 viewController의 라이프 사이클 중 viewWillApper 시점에 call의 parameter인 view와 tabbar를 주입할 것이다.
  • UIViewController.tabBarController 는 viewHierarchy에서 가장 가까운 tabBarController를 반환한다.
  • 이러한 callback을 가진 viewController를 create시 반환하여 기존의 viewController를 대체할 것이다.

Q: 그렇다면 closure의 구현은 어디에서 하나요?
A: 내가 height를 가져다 쓰고 싶은 view에서

Tabbar proxy controller callback

 TabView(selection: $selection) {
         FirstView()
         	.background(TabBarProxy { tabView, tabBar in
                                        print("""
                                              tabbar \(tabBar.bounds)
                                              tabbar-inset \(tabBar.safeAreaInsets)
                                              tabView \(tabView.bounds)
                                              tabview inset \(tabView.safeAreaInsets)
                                              """)
            })
            .tabItem {
                      	Image(systemName: "house")
                      	Text("HOME")
                      }
                      .tag(1)
...

2. NavigationBar height 알아내기

struct NavigationBarProxy: UIViewControllerRepresentable {
    var callback: (UIView, UINavigationBar) -> Void
    private let proxyController = MyViewController()
    
    func makeUIViewController(context: Context) -> UIViewController {
        proxyController.callback = callback
        return proxyController
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        print("update controller")
    }
    
    typealias UIViewControllerType = UIViewController
    
    private class MyViewController: UIViewController {
        var callback: (UIView, UINavigationBar) -> Void = { _, _ in }
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            if let navigationController = self.navigationController {
                self.callback(navigationController.view, navigationController.navigationBar)
            }
        }
    }
}
SomeView()
    .background(NavigationBarProxy { _, navBar in
            print("""
               		navbar \(navBar.bounds)
                    navbar-inset \(navBar.safeAreaInsets)
                  """)
       })

정리하며

결국 이렇게 알아낸 height를 사용하지는 않았지만 이번 기회에 좋은 것을 배웠다. SwiftUI로 넘어갔다고 한들 UIKit에 대해 알지 못하면 한계가 있구나. UIViewRepresentable, UIViewControllerRepresentable을 적재적소에 활용하면 UIKit의 스펙을 무리없이 사용할 수 있어서 다행이다.

profile
느려도 한 걸음 씩 끝까지
post-custom-banner

0개의 댓글