2

We are showing a rectangle with a label over a table view when we encounter an error:

enter image description here

With this code:

    // Fix location of message bar even as user scrolls table - per http://stackoverflow.com/q/7537858/47281
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let newY = self.tableView.contentOffset.y + self.navigationController!.navigationBar.frame.size.height + UIApplication.shared.statusBarFrame.height 
    // Show rectangle using 0 and newY ... 

It works well but doesn't work in some cases such as when the Personal Hotspot is enabled. There is a gap between the Navigation Bar and the status rectangle:

enter image description here

What is the correct way to always position something under the Navigation Bar?

In the first case the status bar height is 20 but in the second case it's 40. In both cases the navigation bar and content offset are the same. Ie:

  • Case #1: Offset: -64.0, Nav Bar height: 44.0 Status Bar Height: 20.0
  • Case #2: Offset: -64.0, Nav Bar height: 44.0 Status Bar Height: 40.0

It seems like the offset isn't being increased based on the change in height in the status bar.

Update: See (my) accepted answer. I'm sure there is a better way.. please add to the thread if you know of one.

Marcus Leon
  • 55,199
  • 118
  • 297
  • 429

3 Answers3

1

The property view.frame is able to capture such change. So, use of view.frame.height for any placement can prevent repositioning.

The way it finds the right height of view with and without hotspot bar:

//with hotspot bar
print (view.frame.height) //540.0   
//without hotspot bar   
print (view.frame.height) //560.0

//setting a navigation bar at top using height of view frame
let navigationBar : UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 0, width:view.frame.width, height: view.frame.height/10))
    navigationBar.backgroundColor =  .gray
navigationBar.setItems([UINavigationItem(title: "navigaion bar")], animated: true)    

let statusbar = UITableView()
//placing the status bar below the navigation bar 
statusbar.frame = CGRect(x: 0, y: navigationBar.frame.size.height, width:view.frame.width, height: view.frame.height/10) //any choice for height:
view.addSubview(statusbar)
view.addSubview(navigationBar)
beginner
  • 11
  • 2
0

Instead of positioning your message with values derived from frames, position it using autolayout.

There are a few ways to implement this - one way you could do this would be the following:

//... assuming this is being called from the parent view controller

    //safely unwrap the reference to the navigation controller
    guard let navController = self.navigationController else { return }
    let message = UIView()

    //arbitrary styles for example only
    message.backgroundColor = UIColor.blue
    //set translatesAutoresizingMaskIntoConstraints so constraints will work
    message.translatesAutoresizingMaskIntoConstraints = false

    //create constraints
    let height = NSLayoutConstraint(item: message, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 50)
    let equalWidth = NSLayoutConstraint(item: message, attribute: NSLayoutAttribute.width, relatedBy: .equal, toItem: navController.navigationBar, attribute: .width, multiplier: 1.0, constant: 0)
    let alignBottom = NSLayoutConstraint(item: message, attribute: .top, relatedBy: .equal, toItem: navController.navigationBar, attribute: .bottom, multiplier: 1, constant: 0)
    //apply constraints
    message.addConstraint(height)
    navController.view.addSubview(message)
    navController.view.addConstraints([equalWidth, alignBottom])

This could also be done using VFL - but its verbosity is maybe a little overkill here.

Otherwise, there are great libs you can use that make this even more declarative and reduce boiler plate - like SnapKit

syllabix
  • 2,240
  • 17
  • 16
0

My "poor man's solution" was to hard code 20 as the status bar height:

 var newY = self.tableView.contentOffset.y
 newY += self.navigationController!.navigationBar.frame.size.height
 newY += 20

Has to be a better way... but this seems to work.

Marcus Leon
  • 55,199
  • 118
  • 297
  • 429