Programmatic beginRefreshing() on iOS11 has proble

2019-05-23 06:37发布

问题:

We have found what seems to be a bug in UIKit but wanted to post here to see if anyone else has this problem or found a solution.

We're trying to use the new iOS11 large titles and hoisted search bar/refreshcontrol. We seemed to have found a problem where the root viewController of the navigation stack shows a minor display issue (problem A) but once another viewcontroller is pushed onto the navigation stack, the display goes nuts (problem B):

Things to note:

  1. The problem is worse on the 2nd VC in the stack rather than the 1st
  2. The refreshControl is not the green color the code sets it to the 1st time you see it on each sceen
  3. The refreshControl slides down as you pull to refresh, it shouldn't do this

This odd behavior seems to only be a problem when we programmatically do a "pull to refresh" in viewDidLoad so that the user can see that the data is loading when they enter the screen. If we remove the lines that invoke refreshControl?.beginRefreshing() the display is clean. I've recreated this problem in a sample vanilla app. This is the entirety of the viewcontroller that shows the problem:

import UIKit

class ViewController: UITableViewController {
    var tableHeaderSearchController: UISearchController!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.navigationController?.navigationBar.prefersLargeTitles = true
        self.navigationController?.navigationItem.largeTitleDisplayMode = .always

        tableHeaderSearchController = UISearchController(searchResultsController: UITableViewController())
        navigationItem.searchController = tableHeaderSearchController

        refreshControl?.tintColor = UIColor.green
        refreshControl?.backgroundColor = UIColor.clear
        refreshControl?.attributedTitle = NSAttributedString(string: "Loading Stuff...", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)])
        refreshControl?.addTarget(self, action: #selector(refreshPulled), for: .valueChanged)

        // Commenting out these 2 lines makes it work fine but you can't see the initial refresh spinner
        refreshControl?.beginRefreshing()
        refreshPulled()
    }

    @objc func refreshPulled() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [refreshControl] in
            refreshControl?.endRefreshing()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

Here's the storyboard. It's just a vanilla tableviewcontroller wrapped in a navigationController. 3 static cells, the 2nd one traverses to another instance of the same controller type.

Any ideas would be greatly appreciated. We'd really like to adopt the new look but this stuff is making it very hard to do so.

回答1:

First, it is absolutely crucial that the table view extend up underneath the navigation bar and that is iOS 11 offset behavior be correct:

    self.edgesForExtendedLayout = .all
    self.tableView.contentInsetAdjustmentBehavior = .always

Second, scrolling to show the refresh control when you refresh manually is up to you, and calculating the amount is not at all simple:

    self.refreshControl!.sizeToFit()
    let top = self.tableView.adjustedContentInset.top
    let y = self.refreshControl!.frame.maxY + top
    self.tableView.setContentOffset(CGPoint(0, -y), animated:true)
    self.refreshControl!.beginRefreshing()

The bar still stays too big during the refresh, but I don't see what can be done about that. Basically Apple has implemented large titles and shown the refresh control in the nav bar without thinking through the effects or dealing with the resulting bugs.