Mostrando una UIProgressView dentro o encima de UINavigationController's UINavigationBar

Quiero tener un UIProgressView para mostrar un progreso en la parte inferior de una barra de navigation (al igual que cuando se envía un iMessage o post de text en iOS 7). Pero necesito esto constantemente en cada vista de vista de tabla de mi controller de navigation. Para mí, estaba claro: tengo que agregar esto al UINavigationController. Pero el problema es que no es posible agregar una UIProgressView al UINavigationController. Entonces probé dos cosas:

Primero traté de agregarlo a la vista de UINavigationController mediante progtwigción. Pero el problema era colocar el UIProgressView y hacer que se vea bien cuando se cambia la rotation del dispositivo.

Lo segundo que intenté es agregar UIProgressView a cada UITableView, pero realmente tengo que hacer esto para cada vista. Además, no se ve bien, porque no está en la parte superior de la barra de navigation, pero debajo. Pero la razón principal por la que no me gustó la segunda solución es porque los ProgressViews van y vienen con su TableView, por lo que no tiene uno estático sino uno que cambia.

Después de esto, no tengo ninguna idea de hacer esto, así que te pregunto … ¿Alguien tiene una idea de cómo hacer esto?

Así es como debería ser: introduzca la descripción de la imagen aquí

Finalmente encontré una solución:

Hice un UINavigationController personalizado y lo agregué a 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]; } 

Creé una nueva UIProgressView (declaré en @interface ), agregué las restricciones para @interface debajo de la barra de navigation y (este paso es importante 🙂 set @interface a @interface en NO .

Volví a trabajar la respuesta del póster original para que la barra esté realmente dentro de la barra de navigation. Lo bueno de esto es que cuando se muestra, se superpone a la línea inferior de un píxel (en efecto, la reemplaza), por lo que al animar la barra de progreso a oculta, la barra de progreso se desvanece y la línea del separador se desvanece. La parte key de esta solución agrega la barra de progreso a la vista del controller de navigation, no a la barra de navigation.

 - (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]; } 

No estoy seguro de por qué es necesario agregar el offset 0.5 al código NSLayoutContstraints para get la misma coincidencia, pero lo es. No utilizo estos formattings visuales, pero la elección es tuya. Tenga en count que la contracción de la parte inferior hace que esta rotation también sea perfecta.

Sobre la base de lo que ya se ha sugerido aquí, si desea que se muestre en todas las barras de navigation de la aplicación, puede convertirlo en una extensión (Swift) en UINavigationController:

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

ACTUALIZACIÓN (utiliza la syntax Swift 3)

Aquí hay una solución un poco más completa. Puse esta extensión en un file llamado UINavigationController + Progress.swift . (Tenga en count que estoy usando la propiedad de la label UIView para encontrar la UIProgressView con la propiedad opcional progressView . Puede haber forms más elegantes de hacerlo, pero esto parece ser lo más sencillo)

 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 } 

Entonces, he dado una implementación específica aquí que supone que desea que se use un conteo actual y un valor de conteo total para actualizar la barra de progreso. Ahora, lo que necesita es publicar la notificación desde el código que está realizando las tareas que se usarán para determinar el progreso, por ejemplo, download una list de files. Aquí está el código para publicar la notificación:

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

Puede agregar ProgressBar en titleView de UInavigationController como se muestra en la siguiente captura de pantalla:

 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; 

SALIDA: NAVIGATIONBAR CON PROGRESSBAR

Espero que te ayude.

Puede hacer esto en la raíz viewController del navigationController:

 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]) } } 

Pero si quiere siempre en la barra de navigation la misma barra de progreso para todos los VCs empujados, entonces es mejor subclass UINavigationController y:

 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]) } 

He descrito cómo hacer esto en mi blog aquí.

TL; DR

  • cree el protocolo que presenta una UIProgressView como la subvista UINavigationController

  • crear protocolo que actualiza UIProgressView

  • conforma tu UINavigationController con el protocolo del presentador
  • conformar cada UIViewController dentro de su stack de navigation al protocolo de actualización