ios - Swift 3、iOS 10 - 以编程方式声明的 UIStackView 不适合屏幕宽度

标签 ios swift uiview swift3 wkwebview

我是一名新的 Swift 开发人员,使用 WKWebView 实现 Safari 的主要部分(我需要在 Javascript 和 Swift 之间进行交互,所以 SFSafariViewController 不是一个选项)并尝试以编程方式声明所有元素。

为了模仿 Safari 的搜索栏和进度条,我想设置一个 UISearchBar,堆叠在 UIProgressView 之上,作为 titleView对于 UIViewControllerUINavigationItem。我可以只用一个元素来管理它,但不能用两个元素的堆栈来管理它。

这是我的项目现在的样子。 UISearchBarUIProgressView 要么太宽要么太薄,无法正确填充 UINavigationBar,具体取决于旋转:

Portrait view Landscape view

这是我的 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.distribution = UIStackViewDistribution.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(string: "https://en.wikipedia.org")!
        webView.load(URLRequest(url: url))
        webView.allowsBackForwardNavigationGestures = true
    }

    /** Updates the UIProgressView. */
    override func observeValue(forKeyPath keyPath: String?, 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.
    }

}

注意:任何已接受的解决方案都必须在回调后继续正确显示元素堆栈,以隐藏由 navigationController?.hidesBarsOnSwipe 启动的 UINavigationBar>,即。当用户在 WKWebView 上执行滑动手势时。

最佳答案

报告我的最终代码,感谢 solution 的指导Hacking With Swift 的作者在 Reddit 上给出:

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(string: "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: String?, 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)!)
        }
    }
}

关于ios - Swift 3、iOS 10 - 以编程方式声明的 UIStackView 不适合屏幕宽度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42183514/

相关文章:

objective-c - 界面生成器 : Resize View From NIB

ios - 在 UITableView Swift 中点击单元格时使用 UIView 创建弹出窗口

ios - 执行 war 的最大重试次数。应用程序加载器问题

ios - 如何改变UItableview的数据?

ios - 触摸并拖动 UIButton,但在松开手指时不触发它

swift - 如何在 Swift 5 中以编程方式更改导航栏的背景颜色?

ios - NSManagedObject 和 Codable 用于存储在服务器和本地存储中的类

ios - 从层次结构中删除 View 时,是否调用了 willMoveToSuperview(使用 nil)?

php - PHP 可以离线访问吗?

iphone - 使用segue将Int从一个ViewController传递到另一个