Search code examples
iosswiftios8-share-extensionlayoutsubviews

Views not laying out correctly in iOS share extension


I have a NavigationVC which I display both in my app and in a share extension, which includes just a search view and a table view. In my app, it appears correctly, but in my share extension, the search view appears 20 pixels too high, and is obscured by the navigation bar. This doesn't make sense, as printing out the frame of the table view, and the search view, and the table view's content inset, there shouldn't be a discrepancy between their appearance. Here's a screenshot when running the app:

enter image description here

And a screenshot when running in the share extension:

enter image description here

Here's the initialisation/layout code I have:

override open func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubview(self.tableView)
    self.view.addSubview(self.searchView)
}

override open func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    self.tableView.frame = self.view.bounds

    var tableViewContentInsetTop: CGFloat

    if let navigationBar = self.navigationController?.navigationBar {
        tableViewContentInsetTop = navigationBar.frame.origin.y + navigationBar.frame.size.height
    } else {
        tableViewContentInsetTop = 0
    }

    self.searchView.frame = CGRect(
        x: 0,
        y: self.tableView.frame.origin.y + tableViewContentInsetTop,
        width: self.tableView.frame.size.width,
        height: NewConversationViewController.searchViewHeight())

    tableViewContentInsetTop += self.searchView.bounds.size.height

    self.tableView.contentInset.top = tableViewContentInsetTop
}

Fairly simple - they're both added to the same view. y coordinate of the bottom of navigation bar is worked out, and search view is placed there. Then the table view has the content inset of the search bar's y in addition to its' height. The result, you'd expect, would be for them to be in alignment. What actually happens is the tableview appears correctly, with the correct content inset, but the search bar appears 20 pixels too high.

The 20 pixels may relate to the fact that the status bar frame (20 pixels high, usually but not always) is not given on a share extension. I printed out some logs in both cases.

Application:

table view frame: (0.0, 0.0, 375.0, 667.0)
search view frame: (0.0, 64.0, 375.0, 40.0)
navigation bar bounds: (0.0, 0.0, 375.0, 44.0)
navigation bar frame: (0.0, 20.0, 375.0, 44.0)
navigation controller frame: (0.0, 0.0, 375.0, 667.0)
status bar frame: (0.0, 0.0, 375.0, 20.0)
application bounds: (0.0, 0.0, 375.0, 667.0)
table view content inset: 104.0

Share extension:

table view frame: (0.0, 0.0, 375.0, 667.0)
search view frame: (0.0, 44.0, 375.0, 40.0)
navigation bar bounds: (0.0, 0.0, 375.0, 44.0)
navigation bar frame: (0.0, 0.0, 375.0, 44.0)
navigation controller frame: (0.0, 0.0, 375.0, 667.0)
status bar frame: (0.0, 0.0, 0.0, 0.0)
application bounds: (0.0, 0.0, 375.0, 667.0)
table view content inset: 84.0

You can see the 20 pixel difference caused by the status bar frame not being accounted for, but that doesn't affect the appearance of the table view - that still appears as expected. It doesn't make sense to me why the search bar would be in a different relative position to it.


Solution

  • Found a solution:

    The status bar frame isn't determined until after viewDidAppear is called, but viewWillLayoutSubviews isn't called at that point. So calling setNeedsLayout in viewDidAppear fixes it.

    As for why it adversely affects the layout of searchView but not tableView, I don't know, as they're both additional properties on a regular UIViewController.