ios – SwiftUI’s new NavigationStack API doesn’t work well with UIHostingController’s nav bar


I have a UIKit app and I migrated some of my screens to SwiftUI using UIHostingController. I used to be able to re-use the same nav bar of UIKit. But after switching to NavigationStack API, I wasn’t able to replicate the same behavior.

Here’s a complete reproducible code:


import UIKit
import SwiftUI

struct ListView: View {
  
  @State var selectedString: String? = nil
  
  var body: some View {
    let details = ["foo", "bar", "baz"]

    // No need to wrap under NavigationView, otherwise will have double Nav Bar
    // This will use the UIKit's nav bar
    List {
      ForEach(details, id: \.self) { detail in
        let destination = Text("This is a detailed page for \(detail)")
          .navigationTitle("Detail page")
        
        NavigationLink(
          detail,
          destination: destination,
          tag: detail,
          selection: $selectedString)
      }
    }
    .navigationTitle("List page")
  }
}

struct ListViewWithNewAPI: View {
  @State var selectedString: String? = nil
  
  var body: some View {
    let details = ["foo", "bar", "baz"]
    
    NavigationStack {
      List(details, id: \.self, selection: $selectedString) { detail in
        NavigationLink(detail, value: detail)
      }
      .navigationDestination(item: $selectedString) { detail in
        Text("This is a detailed page for \(detail)")
          .navigationTitle("Detail page")
      }
      .navigationTitle("List page")
      .navigationBarTitleDisplayMode(.inline)
    }
  }
}

class ViewController: UIViewController {

  @objc
  private func tapButton1() {
    let listVC = UIHostingController(rootView: ListView())
    navigationController?.pushViewController(listVC, animated: true)
  }
  
  @objc
  private func tapButton2() {
    let listVC = UIHostingController(rootView: ListViewWithNewAPI())
    navigationController?.pushViewController(listVC, animated: true)
  }
  
  override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    let button1 = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    button1.backgroundColor = .green
    button1.addTarget(self, action: #selector(tapButton1), for: .touchUpInside)
    view.addSubview(button1)
    
    let button2 = UIButton(frame: CGRect(x: 100, y: 300, width: 100, height: 100))
    button2.backgroundColor = .red
    button2.addTarget(self, action: #selector(tapButton2), for: .touchUpInside)
    view.addSubview(button2)
   
    navigationItem.title = "UIKit title"
  }
}

In the above code, ListView is implemented using the deprecated NavigationView, which works well with UIHostingController. ListViewWithNewAPI is the new implementation using the new NavigationStack API, and I wasn’t able to replicate the original behavior.

Here’s a video comparing the 2 behaviors. Please use the sample code and play around, and see if we can achieve the original behavior using the new API.

enter image description here

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img