Squeak.ru - шаблоны программирования

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

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

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

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

wavy tableview

Я вручную создаю 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 создает квадратное изображение заданного размера и цвета.

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

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


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

Ответы:


1

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

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

    tableView.contentInsetAdjustmentBehavior = .never

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

08.08.2017
  • Есть идеи, почему это влияет только на tablveview, если я установил фоновое изображение на панель навигации? 09.08.2017
  • этот комментарий к определению UIScrollViewContentInsetAdjustmentBehavior.automatic говорит: ... для обратной совместимости также будет регулировать верхний и нижний contentInset, когда представление прокрутки принадлежит контроллеру представления с автоматическиAdjustsScrollViewInsets = YES внутри контроллера навигации, независимо от того, является ли представление прокрутки прокручивается. Моя теория заключается в том, что на contentInset панели навигации влияет установка фонового изображения, которое затем динамически корректируется. 09.08.2017
  • Хм, наверное, ты прав. Спасибо, что указали на документацию, я как-то пропустил это новое свойство. 09.08.2017
  • Вы также можете сделать это с помощью раскадровки. Инспектор размеров - ›Вставки содержимого -› Установите «Никогда». 28.09.2017
  • Если ваш контент выходит за панель вкладок, отключение tableView.contentInsetAdjustmentBehavior приведет к поломке вставок. 29.09.2017
  • Кроме того, простое отключение этого параметра поместит индикатор прокрутки позади (верхней штуковины) на iPhone X в альбомной ориентации. Весь смысл этого поведения состоит в том, чтобы настроить область содержимого прокрутки, чтобы они были видны на экранах, которые не являются прямоугольными. Я думаю, что в настоящее время мы можем увидеть это только на iPhone X Sim. 02.10.2017
  • Любые идеи, почему установка этого на UICollectionView не вступает в силу, как на UITableView? 05.10.2017
  • Как уже упоминалось, это решение вызовет проблемы при работе на iPhone X. К счастью, это была ошибка с iOS 11 и, похоже, решена в 11.0.3, хотя не могу сказать о предыдущих выпусках. 13.10.2017
  • @dvkch Не могли бы вы предоставить ссылку на примечания к выпуску, которые показывают, что это изменение произошло? Исходная проблема с публикациями все еще существует для меня на iOS 11.0.3. 17.10.2017
  • @ Jaun7707 На самом деле это все еще сохраняется, я провел свои последние тесты на scrollView, чей contentSize не заполняет границы ... извините за это. надеюсь, это скоро исправят 17.10.2017
  • Эта проблема была вызвана ошибкой в ​​iOS 11, из-за которой safeAreaInsets представления контроллера представления были установлены неправильно во время перехода навигации, что должно быть исправлено в iOS 11.2. Установка contentInsetAdjustmentBehavior на .never - не лучший обходной путь, потому что он, вероятно, будет иметь другие нежелательные побочные эффекты. Если вы все же используете обходной путь, обязательно удалите его для версий iOS ›= 11.2. 01.11.2017
  • @smileyborg Подтверждено ли, что в iOS 11.2 это поведение изменится? 01.11.2017
  • contentInsetAdjustmentBehavior to. never - не мой вариант. Он появляется, когда у меня есть представление аксессуаров ввода, но когда я возвращаю nil в представлении аксессуаров, он работает нормально. У тебя есть идеи? 03.10.2019

  • 2

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

    ЦЕЛЬ-C

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

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

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

    28.09.2017

    3

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

    if (@available(iOS 11.0, *)) {
          [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } 
    
    09.10.2017
  • Это сломает системный контроллер, например UIImagePickerController. 06.11.2017

  • 4

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

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

    Еще одна вещь, которую стоит попробовать, если ничего не помогает:

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

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

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

    28.10.2017
  • Пробовал почти все в этой теме, и это сработало для меня. TableViewVC встроен в TabBarVC. Спасибо! 25.02.2019

  • 5

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

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

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

  • 6

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

    11.12.2017
  • да, исправлено в iOS 11.2 03.03.2018

  • 7

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

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

  • 8

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

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

    9

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

    self.navigationController.navigationBar.translucent = YES;
    
    12.11.2017

    10

    Удаление лишнего места в верхней части 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)
    }
    
    22.02.2019

    11

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

    self.edgesForExtendedLayout = UIRectEdgeNone
    
    17.10.2017

    12
      if #available(iOS 11, *) {
            self.edgesForExtendedLayout = UIRectEdge.bottom
      }
    

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

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

    22.03.2019
    Новые материалы

    Угловая структура архитектуры
    Обратите внимание, что эта статья устарела, я решил создать новую с лучшей структурой и с учетом автономных компонентов: https://medium.com/@marekpanti/angular-standalone-architecture-b645edd0d54a..

    «Данные, которые большинство людей используют для обучения своих моделей искусственного интеллекта, поставляются со встроенным…
    Первоначально опубликовано HalkTalks: https://hacktown.com.br/blog/blog/os-dados-que-a-maioria-das-pessoas-usa-para-treinar-seus-modelos-de-inteligencia-artificial- ja-vem-com-um-vies-embutido/..

    Сильный ИИ против слабого ИИ: различия парадигм искусственного интеллекта
    В последние годы изучению и развитию искусственного интеллекта (ИИ) уделяется большое внимание и прогресс. Сильный ИИ и Слабый ИИ — две основные парадигмы в области искусственного интеллекта...

    Правильный способ добавить Firebase в ваш проект React с помощью React Hooks
    React + Firebase - это мощная комбинация для быстрого и безопасного создания приложений, от проверки концепции до массового производства. Раньше (знаете, несколько месяцев назад) добавление..

    Создайте API с помощью Python FastAPI
    Создание API с помощью Python становится очень простым при использовании пакета FastAPI. После установки и импорта вы можете создать приложение FastAPI и указать несколько конечных точек. Каждой..

    Веселье с прокси-сервером JavaScript
    Прокси-серверы JavaScript — это чистый сахар, если вы хотите создать некоторую общую логику в своих приложениях, чтобы облегчить себе жизнь. Вот один пример: Связь клиент-сервер Мы..

    Получить бесплатный хостинг для разработчиков | Разместите свой сайт за несколько шагов 🔥
    Статические веб-сайты — это веб-страницы с фиксированным содержанием и его постоянным содержанием. Но теперь статические сайты также обрабатывают динамические данные с помощью API и запросов...