Отображение UIProgressView внутри или поверх UINavigationController UINavigationBar
Я хочу, чтобы UIProgressView показывал прогресс в нижней части панели навигации (точно так же, как при отправке iMessage или текстового сообщения в iOS 7).
Но мне нужно это последовательно на каждом представлении таблицы моего контроллера навигации.
Поэтому для меня было ясно: я должен добавить это в UINavigationController.
Но проблема в том, что UIProgressView нельзя добавлять в UINavigationController.
Поэтому я пробовал две вещи:
1st Я попытался добавить его в UINavigationController для просмотра программно. Но проблема заключалась в том, чтобы позиционировать UIProgressView и сделать его хорошо выглядящим при изменении поворота устройства.
Вторая вещь, которую я пробовал, - это добавить UIProgressView в каждый UITableView, но тогда мне действительно нужно сделать это для каждого представления. Кроме того, он выглядит не очень хорошо, потому что он находится не на панели навигации, а под ней. Но главная причина, по которой мне не понравилось второе решение, - это то, что ProgressViews идут и идут с TableView, поэтому у вас нет статического, но изменяющегося.
После этого у меня нет никакой идеи сделать это, поэтому я прошу вас... у кого-нибудь есть идея, как это сделать?
Как это должно выглядеть:
![enter image description here]()
Ответы
Ответ 1
Наконец я нашел решение:
Я сделал пользовательский 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([email protected])]"
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
) добавил ограничения, чтобы расположить его под панелью навигации и (этот шаг важен:) set translatesAutoresizingMaskIntoConstraints
to NO
.
Ответ 2
Я переработал оригинальный ответ на постер, так что панель фактически находится внутри панели навигации. Что приятно в том, что при его показе он перекрывает нижнюю строку одного пикселя (фактически заменяя ее), поэтому, когда вы анимируете индикатор выполнения до скрытого, индикатор выполнения исчезает, и линия разделителя исчезает. Ключевая часть это решение добавляет индикатор выполнения в вид диспетчера навигации, а не панель навигации.
- (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, чтобы получить одинаковое совпадение, но это так. Я использую эти не визуальные форматы, но выбор за вами. Обратите внимание, что перемещение к днищам делает это также бесшовным во вращении.
Ответ 3
Основываясь на том, что уже было предложено здесь, если вы хотите, чтобы он отображался на каждой панели навигации в приложении, вы можете сделать его в расширение (Swift) на 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)
}
}
UPDATE (использует синтаксис 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)
}
Ответ 4
Вы можете добавить ProgressBar в titleView UInavigationController, как показано ниже в скриншоте:
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;
ВЫХОД: ![NAVIGATIONBAR WITH PROGRESSBAR]()
Надеюсь, это поможет вам.
Ответ 5
Вы можете сделать это в корневом диспетчере 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
Я рассказал, как это сделать в своем сообщении в блоге здесь
TL; DR
-
создать протокол, представляющий UIProgressView в качестве поднабора UINavigationController
-
создать протокол, который обновляет UIProgressView
- совместим ваш UINavigationController с протоколом презентатора
- совместим каждый UIViewController в вашем стеке навигации с протоколом обновления