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

Posted

技术标签:

【中文标题】Swift 3,iOS 10 - 以编程方式声明 UIStackView 不适合屏幕宽度【英文标题】:Swift 3, iOS 10 - programmatically declared UIStackView not fitting screen width 【发布时间】:2017-02-12 02:13:50 【问题描述】:

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

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

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

这是我的 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 上执行滑动手势时。

【问题讨论】:

【参考方案1】:

在Hacking With Swift 的作者在 Reddit 上提供的 solution 的指导下,用我的最终代码进行报告:

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)!)
        
    

【讨论】:

以上是关于Swift 3,iOS 10 - 以编程方式声明 UIStackView 不适合屏幕宽度的主要内容,如果未能解决你的问题,请参考以下文章

Swift 3 - 以编程方式创建标题栏约束

iOS - 在 Swift 3 中以编程方式删除和激活新约束时布局损坏

IOS swift scrollview以编程方式

如何以编程方式从 iOS Swift 3 中的 XIB 推送和呈现给 UIViewController

使用 CoreData 以编程方式创建实体/表,并在 Swift 3 for iOS 中将值插入该实体

iOS Swift:以编程方式安装和卸载视图