Affichage d'un UIProgressView dans ou au-dessus d'UINavigationBar d'UINavigationController

Je veux avoir un UIProgressView pour montrer une progression au bas d'une barre de navigation (comme lorsque vous envoyez un iMessage ou un SMS dans iOS 7). Mais j'ai besoin de cela systématiquement sur chaque vue de table de mon controller de navigation. Donc, pour moi, c'était clair: je dois append ceci à l'UINavigationController. Mais le problème est qu'il n'est pas possible d'append un UIProgressView à UINavigationController. J'ai donc essayé deux choses:

J'ai d'abord essayé de l'append à la vue de UINavigationController par programmation. Mais le problème était de positionner le UIProgressView et de le rendre bien en changeant la rotation du périphérique.

La deuxième chose que j'ai essayé est d'append le UIProgressView à chaque UITableView, mais je dois vraiment le faire pour chaque vue. En outre, il ne semble pas bien, car il n'est pas au-dessus de la barre de navigation, mais en dessous. Mais la principale raison pour laquelle je n'ai pas aimé la 2ème solution est que les ProgressView vont et viennent avec leur TableView, donc vous n'avez pas de static mais en changeant.

Après cela, je n'ai aucune idée de faire cela, alors je vous request … est-ce que quelqu'un a une idée de comment faire cela?

Voilà comment cela devrait ressembler: entrez la description de l'image ici

J'ai finalement trouvé une solution:

J'ai créé un UINavigationController personnalisé et l'ai ajouté à 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 mesortingcs:nil views:NSDictionaryOfVariableBindings(progress, navBar)]]; [[self view] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[progress]|" options:NSLayoutFormatDirectionLeadingToTrailing mesortingcs:nil views:NSDictionaryOfVariableBindings(progress)]]; [progress setTranslatesAutoresizingMaskIntoConstraints:NO]; [progress setProgress:0 animated:NO]; } 

J'ai créé un nouveau UIProgressView (j'ai déclaré dans @interface ) ajouté les contraintes pour le positionner sous la barre de navigation et (cette étape est importante 🙂 mettre translatesAutoresizingMaskIntoConstraints à NO .

J'ai retravaillé la réponse de l'affiche originale pour que la barre soit juste à l'intérieur de la barre de navigation. Ce qui est bien, c'est que quand il est affiché, il chevauche la ligne du bas d'un pixel (en le remplaçant), donc quand vous animez la barre de progression à masqué, la barre de progression disparaît et la ligne de séparation apparaît. cette solution ajoute la barre de progression à la vue du controller de navigation, pas à la barre 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 mesortingcs:nil views:NSDictionaryOfVariableBindings(progress, navBar)]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[progress]|" options:NSLayoutFormatDirectionLeadingToTrailing mesortingcs:nil views:NSDictionaryOfVariableBindings(progress)]]; #else NSLayoutConstraint *constraint; constraint = [NSLayoutConstraint constraintWithItem:progress atsortingbute:NSLayoutAtsortingbuteBottom relatedBy:NSLayoutRelationEqual toItem:navBar atsortingbute:NSLayoutAtsortingbuteBottom multiplier:1 constant:-0.5]; [self.view addConstraint:constraint]; constraint = [NSLayoutConstraint constraintWithItem:progress atsortingbute:NSLayoutAtsortingbuteLeft relatedBy:NSLayoutRelationEqual toItem:navBar atsortingbute:NSLayoutAtsortingbuteLeft multiplier:1 constant:0]; [self.view addConstraint:constraint]; constraint = [NSLayoutConstraint constraintWithItem:progress atsortingbute:NSLayoutAtsortingbuteRight relatedBy:NSLayoutRelationEqual toItem:navBar atsortingbute:NSLayoutAtsortingbuteRight multiplier:1 constant:0]; [self.view addConstraint:constraint]; #endif [progress setTranslatesAutoresizingMaskIntoConstraints:NO]; [progress setProgress:0.5 animated:NO]; } 

Je ne sais pas pourquoi il est nécessaire d'append le décalage 0.5 au code NSLayoutContstraints pour get la même correspondance, mais c'est le cas. Je n'utilise pas ces formats visuels, mais le choix vous appartient. Notez que la contraction au fond rend cette rotation sans couture aussi.

Sur la base de ce qui a déjà été suggéré ici, si vous voulez le faire apparaître sur chaque barre de navigation de l'application, vous pouvez le transformer en extension (Swift) sur 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, mesortingcs: nil, views: ["progressView" : progressView, "navBar" : navBar])) self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[progressView]|", options: .DirectionLeadingToTrailing, mesortingcs: nil, views: ["progressView" : progressView])) progressView.translatesAutoresizingMaskIntoConstraints = false progressView.setProgress(0.5, animated: false) } } 

UPDATE (utilise la syntaxe Swift 3)

Voici une solution un peu plus complète. J'ai mis cette extension dans un file appelé UINavigationController + Progress.swift . (Notez que UIView propriété de balise UIView pour find UIProgressView avec la propriété progressView facultative.Il peut y avoir des façons plus élégantes de faire cela, mais cela semble le plus simple)

 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, mesortingcs: nil, views: ["progressView" : progressView, "navBar" : navBar])) self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[progressView]|", options: .directionLeadingToTrailing, mesortingcs: 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 } 

J'ai donc donné une implémentation spécifique qui suppose que vous voulez un count courant et une valeur de count totale pour mettre à jour la barre de progression. Maintenant, ce dont vous avez besoin est de publier la notification à partir du code qui exécute toutes les tâches à utiliser pour déterminer la progression – par exemple, le téléchargement d'une list de files. Voici le code pour publier la notification:

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

Vous pouvez append ProgressBar dans titleView of UInavigationController comme indiqué dans la capture d'écran ci-dessous:

 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; 

SORTIE: NAVIGATIONBAR AVEC PROGRESSBAR

J'espère que cela vous aide.

Vous pouvez le faire dans la racine viewController de 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, atsortingbute: .Bottom, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Bottom, multiplier: 1, constant: 1) let leftConstraint = NSLayoutConstraint(item: navigationVC.navigationBar, atsortingbute: .Leading, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Leading, multiplier: 1, constant: 0) let rightConstraint = NSLayoutConstraint(item: navigationVC.navigationBar, atsortingbute: .Trailing, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Trailing, multiplier: 1, constant: 0) // add constraints progressBar.translatesAutoresizingMaskIntoConstraints = false navigationVC.view.addConstraints([bottomConstraint, leftConstraint, rightConstraint]) } } 

Mais si vous voulez toujours dans la barre de navigation la même barre de progression pour tous les VCs poussés, il est préférable de sous-classr UINavigationController et:

 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, atsortingbute: .Bottom, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Bottom, multiplier: 1, constant: 1) let leftConstraint = NSLayoutConstraint(item: self.navigationBar, atsortingbute: .Leading, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Leading, multiplier: 1, constant: 0) let rightConstraint = NSLayoutConstraint(item: self.navigationBar, atsortingbute: .Trailing, relatedBy: .Equal, toItem: progressBar, atsortingbute: .Trailing, multiplier: 1, constant: 0) // add constraints progressBar.translatesAutoresizingMaskIntoConstraints = false self.view.addConstraints([bottomConstraint, leftConstraint, rightConstraint]) } 

J'ai expliqué comment faire cela dans mon blog ici

TL; DR

  • créer un protocole qui présente une sous-vue UIProgressView en tant que UINavigationController

  • créer un protocole qui met à jour UIProgressView

  • conformer votre UINavigationController au protocole du présentateur
  • conforme chaque UIViewController dans votre stack de navigation au protocole de mise à jour