subview로 사용할 view의 height를 superview.height - (tabbar.height + navigationbar.height) 로 설정하고 싶었다. UIKit이라면 각각의 viewController에 접근하여 각각의 bound 또는 size를 알아낼 수 있는데 SwiftUI에서는 알아낼 방법이 요원했기에 약간의 편법을 사용했다.
protocol UIViewControllerRepresentable : View where Self.Body == Never
makeUIViewController(context: Context) -> Self.UIViewControllerType
updateUIViewController(Self.UIViewControllerType, context: Self.Context)
dismantleUIViewController(Self.UIViewControllerType, coordinator: Self.Coordinator)
func background<V>(
alignment: Alignment = .center,
content: () -> V
) -> some View where V : View
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)
}
}
}
}
viewWillApper
시점에 call의 parameter인 view와 tabbar를 주입할 것이다.UIViewController.tabBarController
는 viewHierarchy에서 가장 가까운 tabBarController를 반환한다.Q: 그렇다면 closure의 구현은 어디에서 하나요?
A: 내가 height를 가져다 쓰고 싶은 view에서
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)
...
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의 스펙을 무리없이 사용할 수 있어서 다행이다.