Swift 3, iOS 10 – UIStackView a été déclarée par programmation

Je suis un nouveau développeur Swift implémentant les parties principales de Safari avec un WKWebView (j'ai besoin d'interface entre Javascript et Swift, donc SFSafariViewController n'est pas une option) et j'essaye de déclarer tous les éléments par programme.

Pour imiter la barre de search et la barre de progression de Safari, je veux définir un UISearchBar , empilé au-dessus d'un UIProgressView , comme titleView pour UINavigationItem de UIViewController . Je peux le gérer avec un seul élément, mais pas avec une stack de deux éléments.

Voici à quoi ressemble mon projet en ce moment. UISearchBar et UIProgressView sont trop larges ou trop fins pour remplir correctement UINavigationBar , en fonction de la rotation:

Vue portrait Vue du paysage

Voici mon code pour ViewController.swift:

 import WebKit import UIKit class ViewController: UIViewController, WKNavigationDelegate, UISearchBarDelegate { var searchBar: UISearchBar = UISearchBar() var progressView: UIProgressView = UIProgressView(progressViewStyle: .bar) var stackView: UIStackView = UIStackView() var webView: WKWebView! override func loadView() { webView = WKWebView() webView.navigationDelegate = self view = webView } override func viewDidLoad() { super.viewDidLoad() /** Watches for changes in the WKWebView.estimatedProgress variable, and */ webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil) /** Initialise toolbar elements */ let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let refresh = UIBarButtonItem(barButtonSystemItem: .refresh, target: webView, action: #selector(webView.reload)) toolbarItems = [spacer, refresh] navigationController?.isToolbarHidden = false /** Initialise the UISearchBar */ searchBar.delegate = self // not clear yet whether setting this is necessary. searchBar.searchBarStyle = UISearchBarStyle.minimal searchBar.showsCancelButton = true searchBar.widthAnchor.constraint(equalToConstant: (navigationController?.navigationBar.bounds.width)!).isActive = true // searchBar.heightAnchor.constraint(equalToConstant: 44.0).isActive = true // searchBar.sizeToFit() /** Initialise the UIProgressView */ progressView.widthAnchor.constraint(equalToConstant: (navigationController?.navigationBar.bounds.width)!).isActive = true // progressView.heightAnchor.constraint(equalToConstant: 4.0).isActive = true // progressView.sizeToFit() /** Add the UISearchBar & UIProgressView to the UIStackView, then initialise it and finally set it as the UINavigationItem's titleView.*/ stackView.axis = UILayoutConstraintAxis.vertical stackView.alignment = UIStackViewAlignment.center stackView.dissortingbution = UIStackViewDissortingbution.fillProportionally stackView.addArrangedSubview(searchBar) stackView.addArrangedSubview(progressView) stackView.translatesAutoresizingMaskIntoConstraints = false; /* These two constraints are causing a crash, so disabling them for now. */ // stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true // stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true navigationItem.titleView = stackView navigationController?.hidesBarsOnSwipe = true let url = URL(ssortingng: "https://en.wikipedia.org")! webView.load(URLRequest(url: url)) webView.allowsBackForwardNavigationGestures = true } /** Updates the UIProgressView. */ override func observeValue(forKeyPath keyPath: Ssortingng?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // keyPath "estimatedProgress" is equivalent to #keyPath(WKWebView.estimatedProgress) if keyPath == "estimatedProgress" { // progressView.isHidden = webView.estimatedProgress == 1 // if we want to hide upon 100% progressView.progress = Float(webView.estimatedProgress) } } /** Sets the webView's title upon navigation finishing. */ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { title = webView.title } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

Remarque: toute solution acceptée doit continuer à afficher correctement la stack d'éléments après le callback pour masquer le UINavigationBar initié par navigationController?.hidesBarsOnSwipe , ie. lorsqu'un user effectue un mouvement de balayage sur le WKWebView .

Faire un rapport avec mon code final, avec une grande reconnaissance de la solution donnée sur Reddit par l'auteur de Hacking With Swift :

 class ViewController: UIViewController, UISearchBarDelegate { var webView: WKWebView? var searchBar: UISearchBar? var progressView: UIProgressView? override func loadView() { super.loadView() setUpWebView() view = self.webView! // TODO: fix constraints error when video is run. setUpSearchbar() setUpProgressView() let url = URL(ssortingng: "http://www.bbc.com")! webView!.load(URLRequest(url: url)) } func setUpWebView(){ webView = WKWebView() } func setUpSearchbar(){ searchBar = UISearchBar() searchBar!.delegate = self } func setUpProgressView() { webView!.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil) guard let bar = navigationController?.navigationBar else { return; } progressView = UIProgressView(progressViewStyle: .bar) progressView!.translatesAutoresizingMaskIntoConstraints = false bar.addSubview(progressView!) progressView!.leadingAnchor.constraint(equalTo: bar.leadingAnchor).isActive = true progressView!.trailingAnchor.constraint(equalTo: bar.trailingAnchor).isActive = true progressView!.bottomAnchor.constraint(equalTo: bar.bottomAnchor).isActive = true } override func observeValue(forKeyPath keyPath: Ssortingng?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "estimatedProgress" { // progressView.isHidden = webView.estimatedProgress == 1 /* Optional. This hides progressView on 100% */ progressView!.progress = Float((webView?.estimatedProgress)!) } } }