Странное поведение uitableview в iOS11. Ячейки прокручиваются вверх с помощью анимации навигации push

недавно я перенес некоторый код на новый iOS 11 beta 5 SDK.

теперь я получаю очень запутанное поведение от UITableView. Сам tableview не настолько причудлив. У меня есть пользовательские ячейки, но в большинстве случаев это только для их высоты.

когда я нажимаю контроллер вида с tableview, я получаю дополнительную анимацию, где ячейки "прокручиваются" (или, возможно, весь фрейм tableview изменен) и вниз по анимации навигации push/pop. Пожалуйста, смотрите gif:

wavy tableview

Я вручную создать tableview на loadView метод и настройка ограничений автоматической компоновки должны быть равны лидирующему, трейлинговому, верхнему, нижнему супервизору tableview. Супервизор-это корневой вид контроллера вида.

контроллер просмотра, нажимающий код, очень стандартен:self.navigationController?.pushViewController(notifVC, animated: true)

тот же код обеспечивает нормальное поведение на iOS 10.

не могли бы вы мне точку в направлении того, что не так?

EDIT: я сделал очень простой контроллер tableview, и я могу воспроизвести то же поведение там. Код:

class VerySimpleTableViewController : UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = String(indexPath.row)
        cell.accessoryType = .disclosureIndicator

        return cell
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let vc = VerySimpleTableViewController.init(style: .grouped)

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

EDIT 2: я смог сузить проблему до моей настройки UINavigationBar. У меня есть такая настройка:

rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)

здесь createFilledImage создает квадратное изображение с заданным размером и цветом.

если я прокомментирую эту строку, я вернусь к нормальному поведению.

Я был бы признателен за любые мысли по этому вопросу.

10 ответов


это связано с UIScrollView's (UITableView является подклассом UIScrollview) новая contentInsetAdjustmentBehavior свойство, которое имеет значение .automatic по умолчанию.

вы можете переопределить это поведение следующим фрагментом в viewDidLoad любых затронутых контроллеров:

    tableView.contentInsetAdjustmentBehavior = .never

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior


В дополнение к ответу Мэгги по

С

if (@available(iOS 11.0, *)) {
    scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}

эта проблема была вызвана ошибкой в iOS 11, где safeAreaInsets of представление контроллера вида было установлено неправильно во время навигации переход, который должен быть исправлен в iOS 11.2. Установка contentInsetAdjustmentBehavior to .never не лучшее решение потому что это, вероятно, будет иметь другие нежелательные побочные эффекты. Если у вас используйте обходной путь, который вы должны обязательно удалить для версий iOS >= 11.2

-упомянуто smileyborg (инженер-программист в Apple)


вы можете редактировать это поведение сразу во всем приложении, используя NSProxy, например, didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 

Это больше похоже на баг, чем планировалось. Это происходит, когда панель навигации не является полупрозрачной или когда задано фоновое изображение.

Если вы просто установите contentInsetAdjustmentBehavior в .никогда, вставки контента не будут установлены правильно на iPhone X, например, контент будет идти в нижнюю область, под полосами прокрутки.

необходимо сделать две вещи:
1. предотвращение scrollView анимации на push / pop
2. удерживать.автоматическое поведение, потому что это необходимо для iPhone X. Без этого, например, в портрете, контент будет идти ниже нижней полосы прокрутки.

новое простое решение: в XIB: просто добавьте новый UIView поверх вашего основного вида с верхним, ведущим и конечным для просмотра и высоты, установленной в 0. Вам не нужно подключать его к другим подвидам или чему-либо еще.

старое решение:

Примечание: Если вы используете UIScrollView в ландшафтном режиме, он по-прежнему не устанавливает горизонтальные вставки правильно(Еще одна ошибка?), поэтому вы должны закрепить ведущий/трейлинг scrollView в safeAreaInsets В IB.

примечание 2: Решение ниже также имеет проблему, что если tableView прокручивается вниз, и вы нажимаете контроллер и поп назад, он больше не будет внизу.

override func viewDidLoad()
{
    super.viewDidLoad()

    // This parts gets rid of animation when pushing
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidDisappear(_ animated: Bool)
{
    super.viewDidDisappear(animated)
    // This parts gets rid of animation when popping
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidAppear(_ animated: Bool)
{
    super.viewDidAppear(animated)
    // This parts sets correct behaviour(insets are correct on iPhone X)
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .automatic
    }
}

Я могу воспроизвести ошибку для iOS 11.1, но кажется, что ошибка исправлена с iOS 11.2. См.http://openradar.appspot.com/34465226


кроме того, если вы используете панель вкладок, нижняя вставка содержимого представления коллекции будет равна нулю. Для этого поместите ниже код в viewDidAppear:

if #available(iOS 11, *) {
    tableView.contentInset = self.collectionView.safeAreaInsets
}

вот как мне удалось решить эту проблему все еще позволяя iOS 11 автоматически устанавливать вставки. Я использую UITableViewController.

  • выберите "расширить края под верхними барами" и "расширить края под непрозрачными барами" в контроллере вида в раскадровке (или программно). Вставки безопасной области предотвратят ваш взгляд от идти под верхней панелью.
  • Проверьте кнопку "вставки в безопасную область"на виде таблицы в раскадровке. (или tableView.insetsContentViewsToSafeArea = true) - это может быть не обязательно, но это то, что я сделал.
  • установите поведение настройки вставки содержимого в "прокручиваемые оси "(или tableView.contentInsetAdjustmentBehavior = .scrollableAxes) -.always может также работать, но я не стал проверять.

еще одна вещь, чтобы попробовать, если все остальное терпит неудачу:

переопределить viewSafeAreaInsetsDidChange UIViewController метод, чтобы получить представление таблицы, чтобы принудительно установить вставки вида прокрутки в безопасные области. Это в сочетании с "никогда" в Мэгги по ответ.

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    self.tableView.contentInset = self.view.safeAreaInsets;
}

Примечание: self.tableView и self.view должно быть то же самое для UITableViewController


в моем случае это сработало (поместите его в viewDidLoad):

self.navigationController.navigationBar.translucent = YES;

пожалуйста убеждайтесь вместе с вышеуказанным кодом, добавьте дополнительный код следующим образом. Это решило проблему

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}

удалить этот код работа для меня

self.edgesForExtendedLayout = UIRectEdgeNone