[SwiftUI] UIRepresentableViewController内のnavigationBar, barButtonの表示

1 min read読了の目安(約1700字

問題

SwiftUIで構築しているViewから、UIViewControllerのviewをUIRepresentableViewControllerでラップして表示した。
そのUIViewControllerには、viewDidLoad()navigationItemを設置したが、これが表示されない。

表示されないコード
HogeViewControllerを、UIRepresentableViewControllerでラップして、SwiftUIのviewから呼び出して表示させている状況を想定している。

class HogeViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
		let saveBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveBarButtonDidTouchUpInside))
		navigationItem.rightBarButtonItems = [saveBarButtonItem]
	}
	...
}

期待される表示
画面上部の切り抜き。navigationBarにBarButtonItemが表示されている。

原因

1. UIViewControllerRepresentableでラップしたUIViewControllerのnavigationViewControllerは、呼び出し元のSwiftUI(self.parent)にある

SwiftUIは内部的には UINavigationController を使っている。そのため、UIViewControllerをSwiftUIのNavigationViewControllerUIViewControllerRepresentableを使ってプッシュすると、当然そのNavigationControllerはSwiftUIのものを使うことになり、UIViewControllerNavigationControllernilになる。

2. viewWillAppear()にならないとparentにアクセス出来ない

UIViewControllerviewDidLoad()のタイミングでは、親のSwiftUIからはまだ見えない(ラップされてない)、つまりself.parentnilになっているので、viewWillAppear()self.parentにアクセスする。

解決法

class HogeViewController {
	override func viewWillAppear(_ animated: Bool) {
		super.viewWillAppear(animated)		
		let saveBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveBarButtonDidTouchUpInside))
		self.parent?.navigationItem.rightBarButtonItems = [saveBarButtonItem]
	}
	...
}

参考資料

https://stackoverflow.com/questions/59295507/uiviewcontrollerrepresentable-navigation-title-and-bar-button-items-ignored-in