it-swarm.com.ru

Отображение UIProgressView внутри или поверх UINavigationBar UINavigationController

Я хочу, чтобы UIProgressView отображал прогресс в нижней части панели навигации (как при отправке iMessage или текстового сообщения в iOS 7). Но мне нужно это последовательно в каждом табличном представлении моего контроллера навигации. Так что для меня было ясно: я должен добавить это в UINavigationController. Но проблема в том, что невозможно добавить UIProgressView в UINavigationController. Итак, я опробовал две вещи:

Во-первых, я попытался добавить его в представление UINavigationController программным способом. Но проблема заключалась в том, чтобы позиционировать UIProgressView и чтобы он хорошо выглядел при смене устройства.

Второе, что я попробовал, это добавить UIProgressView к каждому UITableView, но тогда мне действительно нужно сделать это для каждого представления. Также это не выглядит хорошо, потому что это не сверху панели навигации, а под ней. Но главная причина, по которой мне не понравилось второе решение, заключается в том, что ProgressViews идут и идут со своим TableView, поэтому у вас нет статического, а только меняющегося.

После этого у меня нет никакой идеи сделать это, поэтому я спрашиваю вас ... у кого-нибудь есть идея, как это сделать?

Вот как это должно выглядеть: enter image description here

14
h345k34cr

Я наконец нашел решение:

Я сделал пользовательский 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), добавил ограничения, чтобы расположить его под панелью навигации, и (этот шаг важен :) установите translatesAutoresizingMaskIntoConstraints в NO.

11
h345k34cr

Я переработал исходный ответ автора, чтобы панель находилась прямо внутри навигационной панели. Что приятно в этом, так это то, что при его отображении он перекрывает нижнюю линию размером в один пиксель (фактически заменяя ее), поэтому, когда вы анимируете индикатор выполнения на скрытый, индикатор выполнения исчезает, а разделительная линия исчезает. Ключевая часть это решение добавляет индикатор выполнения в представление контроллера навигации, а не панель навигации.

- (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, чтобы получить то же совпадение, но это так. Я использую не визуальные форматы, но выбор за вами. Обратите внимание, что в отличие от днищ это также делает это плавным.

11
David H

Основываясь на том, что уже было предложено здесь, если вы хотите, чтобы оно отображалось на каждой панели навигации в приложении, вы можете превратить его в расширение (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)

    }
}

ОБНОВЛЕНИЕ (использует синтаксис 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)
}
3
Matt Long

Вы можете добавить 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

Надеюсь, это поможет вам.

1
Nishant Tyagi

Вы можете сделать это в корневом viewController для 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])
    }
}

Но если вы хотите, чтобы в навигационной панели всегда был один и тот же индикатор выполнения для всех отправленных 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])  
}
1
J.Williams

Я обрисовал в общих чертах, как сделать это в моем сообщении в блоге здесь

TL; DR

  • создать протокол, который представляет UIProgressView в виде подпредставления UINavigationController

  • создать протокол, который обновляет UIProgressView

  • согласовать ваш UINavigationController с протоколом докладчика
  • согласовать каждый UIViewController в вашем стеке навигации с протоколом обновления
0
Harry Bloom