1

I am trying to show a progress bar inside of a UINavigationBar, like discussed here: Showing a UIProgressView inside or on top of a UINavigationController's UINavigationBar.

The custom UINavigationController class looks like this when done in Swift:

class NavigationControllerWithProgress: UINavigationController {

    var progressView: UIProgressView!
    var progressBottomConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        hidesBarsOnSwipe = true
        hidesBarsWhenVerticallyCompact = true

        // adding ProgressView to UINavigationController to allow it to be placed inside the UINavigationBar
        // see: https://stackoverflow.com/questions/19211999/showing-a-uiprogressview-inside-or-on-top-of-a-uinavigationcontrollers-uinaviga
        progressView = UIProgressView(progressViewStyle: UIProgressViewStyle.Bar)
        view.addSubview(progressView)

        progressView.translatesAutoresizingMaskIntoConstraints = false
        progressView.setProgress(0.5, animated: false)

        progressBottomConstraint = NSLayoutConstraint(item: progressView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.navigationBar, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: -0.5)
        view.addConstraint(progressBottomConstraint)

        var constraint: NSLayoutConstraint

        constraint = NSLayoutConstraint(item: progressView, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0)
        view.addConstraint(constraint)

        constraint = NSLayoutConstraint(item: progressView, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0)
        view.addConstraint(constraint)
    }
}

The problem: As soon as the UINavigationBar is hidden automatically by OnSwipe/WhenVerticallyCompact the constraint stops working, i.e. the ProgressView is misplaced under the StatusBar.

I tried to update it in updateViewConstraints() and/or viewWillLayoutSubviews() which is the only way I currently see.

As the constant is already used for the position relative to the UINavigationBar separator (see linked SO thread), i tried this to test the updateViewContraints() for this case:

override func updateViewConstraints() {
    super.updateViewConstraints()

    view.removeConstraint(progressBottomConstraint)
    progressBottomConstraint = NSLayoutConstraint(item: progressView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.navigationBar, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 30)
    view.addConstraint(progressBottomConstraint)
}

Note the constant set to 30 for this test.

From doing this I can see that the constraint does not play nice with the automatic hiding of the UINavigationBar: When hiding/showing the Navigation Bar with Rotation (when vertically compact) or Swipe the Bar is

  • 30 px under the UINavigationBar when in portrait, but
  • at the very top when UINavigationBar is hidden in landscape
  • invisible (covered) when UINavigationBar is shown in landscape

Any suggestions?

Community
  • 1
  • 1
Frederik Winkelsdorf
  • 4,383
  • 1
  • 34
  • 42

1 Answers1

0

While it's possible doing this with auto-layout constraints by code, using a component, here SnapKit, made it even more simple:

override func viewDidLoad() {
    super.viewDidLoad()

    [...]
    progressView.snp_makeConstraints { (make) -> Void in
        progressViewTopConstraint = make.top.equalTo(snp_topLayoutGuideBottom).offset(-2.5).constraint
        make.left.equalTo(view).offset(0)
        make.right.equalTo(view).offset(0)
    }
}

func updateProgressViewConstraint() {
    let navBarHeight = navigationBar.frame.size.height
    progressViewTopConstraint?.updateOffset(navigationBar.hidden ? 0 : navBarHeight - 2.5)
    view.bringSubviewToFront(progressView)
}

override func viewWillLayoutSubviews() {
    updateProgressViewConstraint()
    super.viewWillLayoutSubviews()
}

updateViewConstraints() is the better method to override but that has issues with the new hidesBarOnSwipe in this case (wrong positioned progressView).

Note: The progressView still jumps when doing this in the UINavigationController (i.e. position set after UINavigationBar has finished it's transition), so I moved it to the View itself now.

Edit: Placing it in the View itself fixes the jumping of the bar, but of course does not allow placing the UIProgressView inside the UINavigationBar anymore.

Frederik Winkelsdorf
  • 4,383
  • 1
  • 34
  • 42