【问题标题】:Showing a UIProgressView inside or on top of a UINavigationController's UINavigationBar在 UINavigationController 的 UINavigationBar 内部或顶部显示 UIProgressView
【发布时间】:2013-10-13 06:26:43
【问题描述】:

我想要一个 UIProgressView 在导航栏底部显示进度(就像在 iOS 7 中发送 iMessage 或文本消息时一样)。 但是我在导航控制器的每个表视图视图上都需要这个。 所以对我来说很清楚:我必须将它添加到 UINavigationController。 但问题是,无法将 UIProgressView 添加到 UINavigationController。 所以我尝试了两件事:

第一次我尝试以编程方式将它添加到 UINavigationController 的视图中。但问题是定位 UIProgressView 并使其在更改设备旋转时看起来不错。

我尝试的第二件事是将 UIProgressView 添加到每个 UITableView,但是我真的必须为每个视图都这样做。它看起来也不好看,因为它不在导航栏的顶部,而是在它的下方。但我不喜欢第二种解决方案的主要原因是,ProgressViews 随他们的 TableView 一起出现,所以你没有静态的,而是变化的。

在这之后,我没有任何想法这样做,所以我问你......有人知道如何做到这一点吗?

它应该是这样的:

【问题讨论】:

  • 太棒了,太棒了!!!

标签: ios uitableview uinavigationcontroller uinavigationbar uiprogressview


【解决方案1】:

您可以在 UInavigationController 的 titleView 中添加 ProgressBar,如下图所示:

UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0,0,200,30)];
[customView setBackgroundColor:[UIColor whiteColor]];

    UIProgressView *p = [[UIProgressView alloc] initWithFrame:CGRectMake(10, 20, 180,20)];    // Here pass frame as per your requirement
p.progress = 0.5f;
[customView addSubview:p];
self.navigationItem.titleView = customView;

输出:

希望对你有帮助。

【讨论】:

  • 当我将它添加到导航项时,我(再次)必须将它添加到每个 TableView,对吗?据我所见,UINavigationController 堆栈上的每个视图都用自己的导航项覆盖了导航项。 (我添加了一个屏幕截图来显示结果应该是什么)
【解决方案2】:

我终于找到了解决办法:

我制作了一个自定义 UINavigationController 并将其添加到 viewDidLoad

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    progress = [[UIProgressView alloc] init];

    [[self view] addSubview:progress];

    UIView *navBar = [self navigationBar];


    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[navBar]-[progress(2@20)]"
                                                                        options:NSLayoutFormatDirectionLeadingToTrailing
                                                                        metrics:nil
                                                                          views:NSDictionaryOfVariableBindings(progress, navBar)]];

    [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[progress]|"
                                                                        options:NSLayoutFormatDirectionLeadingToTrailing
                                                                        metrics:nil
                                                                          views:NSDictionaryOfVariableBindings(progress)]];

    [progress setTranslatesAutoresizingMaskIntoConstraints:NO];

    [progress setProgress:0 animated:NO];
}

我创建了一个新的 UIProgressView(我在 @interface 中声明)添加了约束以将其定位在导航栏下方,并且(这一步很重要:)将 translatesAutoresizingMaskIntoConstraints 设置为 NO

【讨论】:

  • 再次,很好的答案 - 没有看到其他人发现将进度条添加到导航控制器的视图是诀窍!太好了!
  • 您好,当您将 UISearchBar 添加到 UITableView 时,我刚刚遇到了一个问题。当您进入搜索栏并且导航栏消失时,它有点搞砸了。 7.0.4 (iPad) 和 7.1 beta 2 (iPhone) 有不同的结果,其中一个进度条移动到 0,0,然后在退出搜索栏时不会重新定位,而另一个则显示在搜索栏上方。我还没有花时间尝试修复它,但想知道您是否遇到过类似的事情,或者您对如何解决它有任何建议?
【解决方案3】:

我修改了原始海报的答案,以便该栏实际上就在导航栏内。这样做的好处是,当它显示时,它与一个像素的底线重叠(实际上是替换它),因此当您将进度条设置为隐藏时,进度条会淡出,而分隔线会淡入。此解决方案是将进度条添加到导航控制器的视图,而不是导航栏。

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    UIProgressView *progress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];;
    [self.view addSubview:progress];
    UINavigationBar *navBar = [self navigationBar];

#if 1
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[navBar]-0-[progress]"
                                                                        options:NSLayoutFormatDirectionLeadingToTrailing
                                                                        metrics:nil
                                                                          views:NSDictionaryOfVariableBindings(progress, navBar)]];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[progress]|"
                                                                        options:NSLayoutFormatDirectionLeadingToTrailing
                                                                        metrics:nil
                                                                            views:NSDictionaryOfVariableBindings(progress)]];
#else
    NSLayoutConstraint *constraint;
    constraint = [NSLayoutConstraint constraintWithItem:progress attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:navBar attribute:NSLayoutAttributeBottom multiplier:1 constant:-0.5];
    [self.view addConstraint:constraint];

    constraint = [NSLayoutConstraint constraintWithItem:progress attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:navBar attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
    [self.view addConstraint:constraint];

    constraint = [NSLayoutConstraint constraintWithItem:progress attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:navBar attribute:NSLayoutAttributeRight multiplier:1 constant:0];
    [self.view addConstraint:constraint];

#endif
    [progress setTranslatesAutoresizingMaskIntoConstraints:NO];
    [progress setProgress:0.5 animated:NO];
}

我不确定为什么有必要将 0.5 偏移量添加到 NSLayoutConstraints 代码以获得相同的匹配,但确实如此。我使用这些不是视觉格式,但选择权在你。请注意,对底部的约束也使旋转变得无缝。

【讨论】:

    【解决方案4】:

    在此处已经建议的基础上,如果您想让它显示在应用程序的每个导航栏上,您可以将其变成 UINavigationController 上的扩展 (Swift):

    extension UINavigationController {
    
        public override func viewDidLoad() {
            super.viewDidLoad()
    
            let progressView = UIProgressView(progressViewStyle: .Bar)
            self.view.addSubview(progressView)
            let navBar = self.navigationBar
    
            self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[navBar]-0-[progressView]", options: .DirectionLeadingToTrailing, metrics: nil, views: ["progressView" : progressView, "navBar" : navBar]))
            self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[progressView]|", options: .DirectionLeadingToTrailing, metrics: nil, views: ["progressView" : progressView]))
    
            progressView.translatesAutoresizingMaskIntoConstraints = false
            progressView.setProgress(0.5, animated: false)
    
        }
    }
    

    更新(使用 Swift 3 语法)

    这里有一个更完整的解决方案。我将此扩展名放入一个名为 UINavigationController+Progress.swift 的文件中。 (请注意,我使用UIView 标签属性来查找UIProgressView 和可选的progressView 属性。可能有更优雅的方法可以做到这一点,但这似乎是最直接的)

    import UIKit
    
    let kProgressViewTag = 10000
    let kProgressUpdateNotification = "kProgressUpdateNotification"
    
    extension UINavigationController {
    
        open override func viewDidLoad() {
            super.viewDidLoad()
    
            let progressView = UIProgressView(progressViewStyle: .bar)
            progressView.tag = kProgressViewTag
            self.view.addSubview(progressView)
            let navBar = self.navigationBar
    
            self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[navBar]-0-[progressView]", options: .directionLeadingToTrailing, metrics: nil, views: ["progressView" : progressView, "navBar" : navBar]))
            self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[progressView]|", options: .directionLeadingToTrailing, metrics: nil, views: ["progressView" : progressView]))
    
            progressView.translatesAutoresizingMaskIntoConstraints = false
            progressView.setProgress(0.0, animated: false)
    
            NotificationCenter.default.addObserver(self, selector: #selector(UINavigationController.didReceiveNotification(notification:)), name: NSNotification.Name(rawValue: kProgressUpdateNotification), object: nil)
        }
    
        var progressView : UIProgressView? {
            return self.view.viewWithTag(kProgressViewTag) as? UIProgressView
        }
    
        func didReceiveNotification(notification:NSNotification) {
            if let progress = notification.object as? ProgressNotification {
                if progress.current == progress.total {
                    self.progressView?.setProgress(0.0, animated: false)
                } else {
                    let perc = Float(progress.current) / Float(progress.total)
                    self.progressView?.setProgress(perc, animated: true)
                }
            }
        }
    }
    
    
    class ProgressNotification {
        var current: Int = 0
        var total:   Int = 0
    
    }
    

    所以我在这里给出了一个具体的实现,假设您希望使用当前计数和总计数值来更新进度条。现在,您需要从正在执行任何用于确定进度的任务的代码中发布通知——例如下载文件列表。这是发布通知的代码:

    let notification = ProgressNotification()
    notification.current = processedTaskCount
    notification.total   = totalTaskCount
    DispatchQueue.main.async {
        NotificationCenter.default.post(name: NSNotification.Name(rawValue: kProgressUpdateNotification), object: notification)
    }
    

    【讨论】:

    • 如何从我的具体 UINavigationController 实现中获得这个 progressView?是否只能通过遍历子视图来实现?
    • 如何获得对进度视图的引用?
    • @Isuru 我用更完整的解决方案更新了我的答案。希望对您有所帮助。
    • @MattLong 非常感谢!我试试看。
    • @MattLong 你有为此维护的任何库吗?我在这里有个问题。我必须在三个屏幕中显示进度视图。每个屏幕都显示进度条应该填充(所以基本上进度条应该填充第三个屏幕。)当用户进入下一个屏幕时,你如何告诉进度条增加它的填充颜色?不确定这将是一个新问题。我很抱歉。
    【解决方案5】:

    您可以在 navigationController 的根 viewController 中执行此操作:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // if VC is pushed in a navigation controller I add a progress bar
        if let navigationVC = self.navigationController {
    
            // create progress bar with .bar style and add it as subview
            let progressBar = UIProgressView(progressViewStyle: .Bar)
            navigationVC.navigationBar.addSubview(self.progressView)
    
            // create constraints
            // NOTE: bottom constraint has 1 as constant value instead of 0; this way the progress bar will look like the one in Safari
            let bottomConstraint = NSLayoutConstraint(item: navigationVC.navigationBar, attribute: .Bottom, relatedBy: .Equal, toItem: progressBar, attribute: .Bottom, multiplier: 1, constant: 1)
            let leftConstraint = NSLayoutConstraint(item: navigationVC.navigationBar, attribute: .Leading, relatedBy: .Equal, toItem: progressBar, attribute: .Leading, multiplier: 1, constant: 0)
            let rightConstraint = NSLayoutConstraint(item: navigationVC.navigationBar, attribute: .Trailing, relatedBy: .Equal, toItem: progressBar, attribute: .Trailing, multiplier: 1, constant: 0)
    
            // add constraints
            progressBar.translatesAutoresizingMaskIntoConstraints = false
            navigationVC.view.addConstraints([bottomConstraint, leftConstraint, rightConstraint])
        }
    }
    

    但是,如果您希望所有推送的 VC 始终在导航栏中显示相同的进度条,那么最好将 UINavigationController 子类化,并且:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // create progress bar with .bar style and keep reference with a property
        let progressBar = UIProgressView(progressViewStyle: .Bar)
        self.progressBar = progressBar
    
        // add progressBar as subview
        self.navigationBar.addSubview(progressBar)
    
        // create constraints
        // NOTE: bottom constraint has 1 as constant value instead of 0; this way the progress bar will look like the one in Safari
        let bottomConstraint = NSLayoutConstraint(item: self.navigationBar, attribute: .Bottom, relatedBy: .Equal, toItem: progressBar, attribute: .Bottom, multiplier: 1, constant: 1)
        let leftConstraint = NSLayoutConstraint(item: self.navigationBar, attribute: .Leading, relatedBy: .Equal, toItem: progressBar, attribute: .Leading, multiplier: 1, constant: 0)
        let rightConstraint = NSLayoutConstraint(item: self.navigationBar, attribute: .Trailing, relatedBy: .Equal, toItem: progressBar, attribute: .Trailing, multiplier: 1, constant: 0)
    
        // add constraints
        progressBar.translatesAutoresizingMaskIntoConstraints = false
        self.view.addConstraints([bottomConstraint, leftConstraint, rightConstraint])  
    }
    

    【讨论】:

      【解决方案6】:

      我已经在我的博文here中概述了如何做到这一点

      TL;DR

      • 创建将 UIProgressView 呈现为 UINavigationController 子视图的协议

      • 创建更新 UIProgressView 的协议

      • 使您的 UINavigationController 符合演示者协议
      • 使导航堆栈中的每个 UIViewController 都符合更新程序协议

      【讨论】:

        【解决方案7】:

        SnapKit 用户的解决方案。还将progressView 放在navigationBar 上方。

        extension UINavigationController {
        
            public override func viewDidLoad() {
                super.viewDidLoad()
        
                let progressView = UIProgressView(progressViewStyle: .bar)
                self.view.addSubview(progressView)
                let navBar = self.navigationBar
        
                progressView.snp.makeConstraints { make in
                    make.leading.trailing.equalToSuperview()
                    make.top.equalTo(navBar.snp.top)
                }
                progressView.setProgress(0.5, animated: false)
        
                view.bringSubviewToFront(progressView)
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-06-06
          • 2023-04-01
          • 1970-01-01
          • 2011-12-25
          • 2012-11-26
          • 1970-01-01
          • 2016-05-17
          • 1970-01-01
          相关资源
          最近更新 更多