17

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

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

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

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

它应该是这样的: 在此处输入图像描述

4

7 回答 7

12

我终于找到了解决方案:

我制作了一个自定义 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)添加了约束以将其定位在导航栏下方,并且(这一步很重要:)设置translatesAutoresizingMaskIntoConstraintsNO

于 2013-10-07T18:20:30.170 回答
12

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

- (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 偏移量添加到 NSLayoutContstraints 代码以获得相同的匹配,但确实如此。我使用这些不是视觉格式,但选择权在你。请注意,对底部的约束也使旋转无缝。

于 2013-10-21T14:55:59.673 回答
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的文件中。(请注意,我使用UIViewtag 属性来查找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)
}
于 2015-10-17T21:42:22.330 回答
2

您可以在 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])  
}
于 2015-11-09T10:02:26.987 回答
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;

输出 :带进度条的导航栏

希望它可以帮助你。

于 2013-10-06T18:06:27.573 回答
1

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)
    }
}
于 2020-04-02T18:34:24.040 回答
0

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

TL;博士

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

  • 创建更新 UIProgressView 的协议

  • 使您的 UINavigationController 符合演示者协议
  • 使导航堆栈中的每个 UIViewController 符合更新程序协议
于 2017-04-06T15:13:18.257 回答