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

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

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

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

wavy tableview

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

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

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

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

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

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)
    }
}

ИЗМЕНИТЬ 2: я смог сузить вопрос до моей настройки UINavigationBar. У меня есть такая настройка:

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

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

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

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

Ответ 1

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

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

    tableView.contentInsetAdjustmentBehavior = .never

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

Ответ 2

В дополнение к маггическому ответу

OBJECTIVE-C

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

Ответ 3

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

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

Ответ 4

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

Если вы просто установили contentInsetAdjustmentBehavior на .never, вставки iPhone не будут корректно установлены на 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
    }
}

Ответ 5

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

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

Ответ 6

Удалите этот код для меня

self.edgesForExtendedLayout = UIRectEdgeNone

Ответ 7

Вот как мне удалось исправить эту проблему , но при этом разрешить 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

Ответ 8

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

self.navigationController.navigationBar.translucent = YES;

Ответ 9

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

Ответ 10

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

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