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

116

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

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

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

волнистый вид на стол

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

Код отправки контроллера представления очень стандартный: self.navigationController?.pushViewController(notifVC, animated: true)

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

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

РЕДАКТИРОВАТЬ: Я сделал очень простой контроллер 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)
    }
}

РЕДАКТИРОВАТЬ 2: мне удалось сузить проблему до моей настройки UINavigationBar. У меня есть такая настройка:

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

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

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

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

Iur
источник
Возможно, это не проблема с настройкой панели навигации. У меня была такая же проблема (принятый ответ решил это) без какой-либо настройки. Я думаю, что это может быть проблема с тем, как iOS обрабатывает табличное представление, когда оно создается вручную как подпредставление, вместо использования UITableViewController.
Марк Леонард
2
Я вижу это поведение только тогда , когда я поставил navigationBar.isTranslucentв falseпротивном случае он работает отлично.
b_ray
5
Похоже, это ошибка в iOS11 GM, пожалуйста, дублируйте этот отчет об ошибке, чтобы эта проблема привлекла внимание Apple: openradar.appspot.com/34465226
b_ray
1
Эта проблема, похоже, исправлена ​​в бета-версии iOS 11.2. Я бы не стал устанавливать contentInsetAdjustmentBehavior на never, потому что он нарушает прокрутку iPhone X, не добавляя отступы в нижней части экрана. Нижняя часть экрана содержимого остается под «кнопкой» главного экрана iPhone X.
Бат

Ответы:

150

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

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

    tableView.contentInsetAdjustmentBehavior = .never

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

Мэгги Хиллен
источник
2
в этом комментарии к определению UIScrollViewContentInsetAdjustmentBehavior.automatic говорится: «... для обратной совместимости также будет регулироваться верхний и нижний contentInset, когда представление прокрутки принадлежит контроллеру представления с автоматическиAdjustsScrollViewInsets = YES внутри контроллера навигации, независимо от того, прокручивается ли прокрутка вид можно прокручивать ". Моя теория заключается в том, что на contentInset панели навигации влияет установка фонового изображения, которое затем динамически корректируется.
Мэгги Хиллен
8
Вы также можете сделать это с помощью раскадровки. Инспектор размеров -> Вставки содержимого -> Установить "Никогда".
Woongbi Kim
4
Если ваш контент выходит за пределы панели вкладок, отключение tableView.contentInsetAdjustmentBehaviorприведет к поломке вставок.
Кин
4
Кроме того, простое отключение этого параметра поместит индикатор прокрутки за (верхней штуковиной) на iPhone X в альбомной ориентации. Весь смысл этого поведения состоит в том, чтобы настроить область содержимого прокрутки, чтобы они были видны на экранах, которые не являются прямоугольными. Я думаю, что в настоящее время мы можем увидеть это только на iPhone X Sim.
PhoneyDeveloper
8
Эта проблема была вызвана ошибкой в ​​iOS 11, когда safeAreaInsets представления контроллера представления были установлены неправильно во время перехода навигации, что должно быть исправлено в iOS 11.2. Установка contentInsetAdjustmentBehaviorдля .neverне большой обходного пути , потому что он, вероятно , есть и другие нежелательные побочные эффекты. Если вы используете обходной путь, обязательно удалите его для версий iOS> = 11.2.
смайлик
23

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

Objective-C

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

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

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

Лал Кришна
источник
6

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

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
BigDanceMouse
источник
1
Контроллер Эта система будет перерыв, как и UIImagePickerController
Galway
6

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

Дед Мороз
источник
Пробовал почти все в этой теме, и это сработало для меня. TableViewVC встроен в TabBarVC. Спасибо!
Джош Вольф
3

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

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

Необходимо сделать две вещи:
1. предотвратить анимацию scrollView при нажатии / попа
2. сохранить .automatic поведение, потому что это необходимо для 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
    }
}
Эль Ужасный
источник
Что такое parentView?
PhoneyDeveloper 02
Основной вид вашего контроллера представления, я обновил ответ. Спасибо.
Эль Ужасный
Когда я использую UITableViewController, tableView является представлением контроллера представления. Также вставки должны отличаться при повороте устройства. Я хочу поправиться. Мне просто не нравится анимация при первом появлении tableView.
PhoneyDeveloper
В вашем случае, поскольку UITableView является представлением контроллера, он должен иметь safeAreaInsets.bottom = 34 (портрет), поэтому вы можете просто установить tableView.contentInset = tableView.safeAreaInsets.
Эль Ужасный
У меня это не работает. В viewDidLoad все вставки равны нулю. Если я попытаюсь использовать этот код в viewWillLayoutSubviews, индикатор прокрутки будет вставлен, но сам tableView сможет прокручиваться по горизонтали. Если я просто посмотрю на вставки в viewWillLayoutSubviews, не отключая регулировку, то нижний AdjustContentInset станет 21, и это все, что, похоже, изменится.
PhoneyDeveloper
3

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

Линда
источник
да, исправлено на iOS 11.2
Richie Hyatt
2

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

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
Басаварадж Калагатаги
источник
Только это решило мою проблему !! Спасибо. Слишком много времени потрачено на эту проблему!
Murat
2

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

if #available(iOS 11, *) {
    tableView.contentInset = self.collectionView.safeAreaInsets
}
hasankose
источник
2

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

self.navigationController.navigationBar.translucent = YES;
nemissm
источник
1

Удаление лишнего места в верхней части collectionViewилиtableView

    if #available(iOS 11.0, *) {
        collectionView.contentInsetAdjustmentBehavior  = .never
        //tableView.contentInsetAdjustmentBehavior  = .never
    } else {
        automaticallyAdjustsScrollViewInsets = false
    }

Над кодом collectionViewили tableViewидет под панелью навигации.
Код ниже не позволяет отображать коллекцию под навигацией

    self.edgesForExtendedLayout = UIRectEdge.bottom

но я люблю использовать приведенную ниже логику и код для UICollectionView

Значения вставки краев применяются к прямоугольнику для сжатия или расширения области, представленной этим прямоугольником. Обычно во время компоновки вида для изменения его рамки используются вставки по краям. Положительные значения приводят к смещению (или сжатию) кадра на указанную величину. Отрицательные значения приводят к смещению (или расширению) кадра на указанную величину.

collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)

Лучший способ для UICollectionView

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}
Назмул Хасан
источник
0

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

self.edgesForExtendedLayout = UIRectEdgeNone
weiminghuaa
источник
0
  if #available(iOS 11, *) {
        self.edgesForExtendedLayout = UIRectEdge.bottom
  }

Я использовал UISearchController с настраиваемыми контроллерами результатов, которые имеют табличное представление. Нажатие нового контроллера на контроллер результатов заставило tableview перейти в поиск.

Приведенный выше код полностью устранил проблему.

Виталий Швецов
источник