在UINavigationController的UINavigationBar内部或顶部显示一个UIProgressView

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

第一我试图以编程方式将其添加到UINavigationController的视图。 但问题是定位UIProgressView,并使其看起来不错,当改变设备的旋转。

我尝试的第二件事是将UIProgressView添加到每个UITableView,但是我真的必须为每个视图执行此操作。 它也不好看,因为它不在导航栏的顶部,而是在它的下面。 但是,我不喜欢第二种解决scheme的主要原因是因为ProgressViews与他们的TableView一起来,所以你没有一个静态的,但改变了。

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

这就是它应该是这样的: 在这里输入图像说明

我终于find了解决办法:

我做了一个自定义的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]; } 

我创build了一个新的UIProgressView(我在@interface声明)添加了约束来将其放在导航栏的下面,(这一步很重要:)将translatesAutoresizingMaskIntoConstraints设置为NO

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

 - (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代码来获得相同的匹配,但它是。 我使用这些不是视觉格式,但select是你的。 请注意,反对底部也使得这无缝循环。

基于这里已经build议的内容,如果你想让它显示在应用程序的每个导航栏上,你可以在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语法)

这是一个更完整的解决scheme。 我把这个扩展名放到一个名为UINavigationController + Progress.swift的文件中。 (注意我正在使用UIView标签属性来查找带有可选progressView属性的UIProgressView ,可能有更好的方法可以做到这一点,但这似乎是最直接的)

 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的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; 

输出: 带有进度条的导航栏

希望它可以帮助你。

您可以在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]) } 

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

TL; DR

  • 创build一个UIProgressView作为UINavigationController子视图的协议

  • 创build更新UIProgressView的协议

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